@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.
@@ -1,5 +1,5 @@
1
1
  import * as React3 from 'react';
2
- import { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useContext } from 'react';
2
+ import { forwardRef, useReducer, useEffect, createContext, useRef, useMemo, useCallback, useImperativeHandle, useLayoutEffect, useState, useContext } from 'react';
3
3
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
4
  import * as ReactDOM from 'react-dom';
5
5
  import { flushSync } from 'react-dom';
@@ -49,6 +49,12 @@ function isHorizontalRTL(state) {
49
49
  function isHorizontalRTLProps(props) {
50
50
  return !!(props == null ? void 0 : props.horizontal) && isRTLProps(props);
51
51
  }
52
+ function getStylePaddingEnd(props) {
53
+ if (!(props == null ? void 0 : props.horizontal)) {
54
+ return (props == null ? void 0 : props.stylePaddingBottom) || 0;
55
+ }
56
+ return (isHorizontalRTLProps(props) ? props.stylePaddingLeft : props.stylePaddingRight) || 0;
57
+ }
52
58
  function getLogicalHorizontalMaxOffset(state, contentWidth) {
53
59
  var _a3;
54
60
  return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
@@ -141,11 +147,13 @@ function StateProvider({ children }) {
141
147
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
142
148
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
143
149
  mapViewabilityValues: /* @__PURE__ */ new Map(),
150
+ pendingContainerIds: void 0,
144
151
  positionListeners: /* @__PURE__ */ new Map(),
145
152
  scrollAxisGap: 0,
146
153
  state: void 0,
147
154
  values: /* @__PURE__ */ new Map([
148
155
  ["alignItemsAtEndPadding", 0],
156
+ ["containerLayoutEpoch", 0],
149
157
  ["stylePaddingTop", 0],
150
158
  ["headerSize", 0],
151
159
  ["numContainers", 0],
@@ -167,6 +175,14 @@ function useStateContext() {
167
175
  return React3.useContext(ContextState);
168
176
  }
169
177
  function createSelectorFunctionsArr(ctx, signalNames) {
178
+ if (!ctx) {
179
+ const emptyValues = [];
180
+ return {
181
+ get: () => emptyValues,
182
+ subscribe: () => () => {
183
+ }
184
+ };
185
+ }
170
186
  let lastValues = [];
171
187
  let lastSignalValues = [];
172
188
  return {
@@ -273,7 +289,7 @@ function getContentInsetEndAdjustmentEnd(adjustment) {
273
289
  return Math.max(0, adjustment != null ? adjustment : 0);
274
290
  }
275
291
  function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
276
- var _a3, _b;
292
+ var _a3;
277
293
  const state = ctx.state;
278
294
  const { props } = state;
279
295
  const horizontal = props.horizontal;
@@ -284,8 +300,8 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
284
300
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
285
301
  );
286
302
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
287
- const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
288
- const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
303
+ const anchoredEndInset = props.anchoredEndSpace && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
304
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
289
305
  const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
290
306
  if (overrideInset) {
291
307
  const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
@@ -301,14 +317,15 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
301
317
  function getContentSize(ctx) {
302
318
  var _a3, _b;
303
319
  const { values, state } = ctx;
304
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
305
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
320
+ const stylePaddingStart = state.props.horizontal ? state.props.stylePaddingLeft || 0 : values.get("stylePaddingTop") || 0;
321
+ const stylePaddingEnd = state.props.horizontal ? state.props.stylePaddingRight || 0 : state.props.stylePaddingBottom || 0;
306
322
  const alignItemsAtEndPadding = values.get("alignItemsAtEndPadding") || 0;
307
323
  const headerSize = values.get("headerSize") || 0;
308
324
  const footerSize = values.get("footerSize") || 0;
309
- const contentInsetBottom = getContentInsetEnd(ctx);
325
+ const contentInsetEnd = getContentInsetEnd(ctx);
310
326
  const totalSize = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize");
311
- return headerSize + footerSize + totalSize + stylePaddingTop + alignItemsAtEndPadding + stylePaddingBottom + (contentInsetBottom || 0);
327
+ const layoutSize = Math.max(0, totalSize - (state.props.data.length > 0 ? ctx.scrollAxisGap : 0));
328
+ return headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd + (contentInsetEnd || 0);
312
329
  }
313
330
 
314
331
  // src/components/DebugView.tsx
@@ -370,6 +387,18 @@ function useInterval(callback, delay) {
370
387
  }, [delay]);
371
388
  }
372
389
 
390
+ // src/constants-platform.ts
391
+ var IsNewArchitecture = true;
392
+
393
+ // src/core/containerLayoutBaseline.ts
394
+ var containerLayoutBaselines = /* @__PURE__ */ new WeakMap();
395
+ function getContainerLayoutBaseline(element) {
396
+ return containerLayoutBaselines.get(element);
397
+ }
398
+ function setContainerLayoutBaseline(element, size) {
399
+ containerLayoutBaselines.set(element, size);
400
+ }
401
+
373
402
  // src/utils/devEnvironment.ts
374
403
  var metroDev = typeof __DEV__ !== "undefined" ? __DEV__ : void 0;
375
404
  var _a;
@@ -383,520 +412,239 @@ var POSITION_OUT_OF_VIEW = -1e7;
383
412
  var EDGE_POSITION_EPSILON = 1;
384
413
  var ENABLE_DEVMODE = IS_DEV && false;
385
414
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
386
- var typedForwardRef = React3.forwardRef;
387
- var typedMemo = React3.memo;
388
- var getComponent = (Component) => {
389
- if (React3.isValidElement(Component)) {
390
- return Component;
391
- }
392
- if (Component) {
393
- return /* @__PURE__ */ React3.createElement(Component, null);
394
- }
395
- return null;
396
- };
397
415
 
398
- // src/utils/helpers.ts
399
- function isFunction(obj) {
400
- return typeof obj === "function";
401
- }
402
- function isArray(obj) {
403
- return Array.isArray(obj);
416
+ // src/core/deferredPublicOnScroll.ts
417
+ function withResolvedContentOffset(state, event, resolvedOffset) {
418
+ return {
419
+ ...event,
420
+ nativeEvent: {
421
+ ...event.nativeEvent,
422
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
423
+ }
424
+ };
404
425
  }
405
- var warned = /* @__PURE__ */ new Set();
406
- function warnDevOnce(id, text) {
407
- if (IS_DEV && !warned.has(id)) {
408
- warned.add(id);
409
- console.warn(`[legend-list] ${text}`);
426
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
427
+ var _a3, _b, _c, _d;
428
+ const state = ctx.state;
429
+ const deferredEvent = state.deferredPublicOnScrollEvent;
430
+ state.deferredPublicOnScrollEvent = void 0;
431
+ if (deferredEvent) {
432
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
433
+ _c,
434
+ withResolvedContentOffset(
435
+ state,
436
+ deferredEvent,
437
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
438
+ )
439
+ );
410
440
  }
411
441
  }
412
- function roundSize(size) {
413
- return Math.floor(size * 8) / 8;
442
+
443
+ // src/core/initialScrollSession.ts
444
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
445
+ function hasInitialScrollSessionCompletion(completion) {
446
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
414
447
  }
415
- function isNullOrUndefined(value) {
416
- return value === null || value === void 0;
448
+ function clearInitialScrollSession(state) {
449
+ state.initialScrollSession = void 0;
450
+ return void 0;
417
451
  }
418
- function getPadding(s, type) {
419
- var _a3, _b, _c;
420
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
421
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
452
+ function createInitialScrollSession(options) {
453
+ const { bootstrap, completion, kind, previousDataLength } = options;
454
+ return kind === "offset" ? {
455
+ completion,
456
+ kind,
457
+ previousDataLength
458
+ } : {
459
+ bootstrap,
460
+ completion,
461
+ kind,
462
+ previousDataLength
463
+ };
422
464
  }
423
- function extractPadding(style, contentContainerStyle, type) {
424
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
465
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
466
+ var _a4, _b2;
467
+ if (!state.initialScrollSession) {
468
+ state.initialScrollSession = createInitialScrollSession({
469
+ completion: {},
470
+ kind,
471
+ previousDataLength: 0
472
+ });
473
+ } else if (state.initialScrollSession.kind !== kind) {
474
+ state.initialScrollSession = createInitialScrollSession({
475
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
476
+ completion: state.initialScrollSession.completion,
477
+ kind,
478
+ previousDataLength: state.initialScrollSession.previousDataLength
479
+ });
480
+ }
481
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
482
+ return state.initialScrollSession.completion;
425
483
  }
426
- function findContainerId(ctx, key) {
427
- var _a3, _b;
428
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
429
- if (directMatch !== void 0) {
430
- return directMatch;
484
+ var initialScrollCompletion = {
485
+ didDispatchNativeScroll(state) {
486
+ var _a3, _b;
487
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
488
+ },
489
+ didRetrySilentInitialScroll(state) {
490
+ var _a3, _b;
491
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
492
+ },
493
+ markInitialScrollNativeDispatch(state) {
494
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
495
+ },
496
+ markSilentInitialScrollRetry(state) {
497
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
498
+ },
499
+ resetFlags(state) {
500
+ if (!state.initialScrollSession) {
501
+ return;
502
+ }
503
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
504
+ completion.didDispatchNativeScroll = void 0;
505
+ completion.didRetrySilentInitialScroll = void 0;
431
506
  }
432
- const numContainers = peek$(ctx, "numContainers");
433
- for (let i = 0; i < numContainers; i++) {
434
- const itemKey = peek$(ctx, `containerItemKey${i}`);
435
- if (itemKey === key) {
436
- return i;
507
+ };
508
+ var initialScrollWatchdog = {
509
+ clear(state) {
510
+ initialScrollWatchdog.set(state, void 0);
511
+ },
512
+ didReachTarget(newScroll, watchdog) {
513
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
514
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
515
+ },
516
+ get(state) {
517
+ var _a3, _b;
518
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
519
+ },
520
+ hasNonZeroTargetOffset(targetOffset) {
521
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
522
+ },
523
+ isAtZeroTargetOffset(targetOffset) {
524
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
525
+ },
526
+ set(state, watchdog) {
527
+ var _a3, _b;
528
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
529
+ return;
437
530
  }
531
+ const completion = ensureInitialScrollSessionCompletion(state);
532
+ completion.watchdog = watchdog ? {
533
+ startScroll: watchdog.startScroll,
534
+ targetOffset: watchdog.targetOffset
535
+ } : void 0;
438
536
  }
439
- return -1;
537
+ };
538
+ function setInitialScrollSession(state, options = {}) {
539
+ var _a3, _b, _c, _d;
540
+ const existingSession = state.initialScrollSession;
541
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
542
+ const completion = existingSession == null ? void 0 : existingSession.completion;
543
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
544
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
545
+ if (!kind) {
546
+ return clearInitialScrollSession(state);
547
+ }
548
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
549
+ return clearInitialScrollSession(state);
550
+ }
551
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
552
+ state.initialScrollSession = createInitialScrollSession({
553
+ bootstrap,
554
+ completion,
555
+ kind,
556
+ previousDataLength
557
+ });
558
+ return state.initialScrollSession;
440
559
  }
441
560
 
442
- // src/components/PositionView.tsx
443
- var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
444
- var baseCss = {
445
- contain: "paint layout style",
446
- ...isRNWeb ? {
447
- display: "flex",
448
- flexDirection: "column"
449
- } : {}
561
+ // src/utils/checkThreshold.ts
562
+ var HYSTERESIS_MULTIPLIER = 1.3;
563
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
564
+ const absDistance = Math.abs(distance);
565
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
566
+ }
567
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
568
+ const absDistance = Math.abs(distance);
569
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
570
+ const updateSnapshot = () => {
571
+ setSnapshot({
572
+ atThreshold,
573
+ contentSize: context.contentSize,
574
+ dataLength: context.dataLength,
575
+ scrollPosition: context.scrollPosition
576
+ });
577
+ };
578
+ if (!wasReached) {
579
+ if (!within) {
580
+ return false;
581
+ }
582
+ onReached(distance);
583
+ updateSnapshot();
584
+ return true;
585
+ }
586
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
587
+ if (reset) {
588
+ setSnapshot(void 0);
589
+ return false;
590
+ }
591
+ if (within) {
592
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
593
+ if (changed) {
594
+ updateSnapshot();
595
+ }
596
+ }
597
+ return true;
450
598
  };
451
- var PositionViewState = typedMemo(function PositionViewState2({
452
- id,
453
- horizontal,
454
- style,
455
- refView,
456
- ...props
457
- }) {
458
- const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
459
- const composed = isArray(style) ? Object.assign({}, ...style) : style;
460
- const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
461
- const {
462
- animatedScrollY: _animatedScrollY,
463
- index,
464
- onLayout: _onLayout,
465
- onLayoutChange: _onLayoutChange,
466
- stickyHeaderConfig: _stickyHeaderConfig,
467
- ...webProps
468
- } = props;
469
- return /* @__PURE__ */ React3.createElement("div", { "data-index": index, ref: refView, ...webProps, style: combinedStyle });
470
- });
471
- var PositionViewSticky = typedMemo(function PositionViewSticky2({
472
- id,
473
- horizontal,
474
- style,
475
- refView,
476
- index,
477
- animatedScrollY: _animatedScrollY,
478
- stickyHeaderConfig,
479
- onLayout: _onLayout,
480
- onLayoutChange: _onLayoutChange,
481
- children,
482
- ...webProps
483
- }) {
484
- const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([
485
- `containerPosition${id}`,
486
- "activeStickyIndex"
487
- ]);
488
- const composed = React3.useMemo(
489
- () => {
490
- var _a3;
491
- return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
492
- },
493
- [style]
494
- );
495
- const viewStyle = React3.useMemo(() => {
496
- var _a3;
497
- const styleBase = { ...baseCss, ...composed };
498
- delete styleBase.transform;
499
- const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
500
- const isActive = activeStickyIndex === index;
501
- styleBase.position = isActive ? "sticky" : "absolute";
502
- styleBase.zIndex = index + 1e3;
503
- if (horizontal) {
504
- styleBase.left = isActive ? offset : position;
505
- } else {
506
- styleBase.top = isActive ? offset : position;
507
- }
508
- return styleBase;
509
- }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
510
- const renderStickyHeaderBackdrop = React3.useMemo(
511
- () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3.createElement(
512
- "div",
513
- {
514
- style: {
515
- inset: 0,
516
- pointerEvents: "none",
517
- position: "absolute"
518
- }
519
- },
520
- getComponent(stickyHeaderConfig.backdropComponent)
521
- ) : null,
522
- [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
523
- );
524
- return /* @__PURE__ */ React3.createElement(
525
- "div",
526
- {
527
- "data-index": index,
528
- ref: refView,
529
- style: viewStyle,
530
- ...webProps
531
- },
532
- renderStickyHeaderBackdrop,
533
- children
534
- );
535
- });
536
- var PositionView = PositionViewState;
537
-
538
- // src/constants-platform.ts
539
- var IsNewArchitecture = true;
540
- function useInit(cb) {
541
- useState(() => cb());
542
- }
543
-
544
- // src/state/ContextContainer.ts
545
- var ContextContainer = createContext(null);
546
- var NO_CONTAINER_ID = -1;
547
- function useContextContainer() {
548
- return useContext(ContextContainer);
549
- }
550
- function useContainerItemInfo(containerContext) {
551
- var _a3;
552
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
553
- const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
554
- return containerContext ? itemInfo : void 0;
555
- }
556
- function useContainerItemKey(containerContext) {
557
- var _a3;
558
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
559
- const [itemKey] = useArr$([`containerItemKey${containerId}`]);
560
- return containerContext ? itemKey : void 0;
561
- }
562
- function useAdaptiveRender() {
563
- const [mode] = useArr$(["adaptiveRender"]);
564
- return mode;
565
- }
566
- function useAdaptiveRenderChange(callback) {
567
- const ctx = useStateContext();
568
- const callbackRef = useRef(callback);
569
- callbackRef.current = callback;
570
- useLayoutEffect(() => {
571
- let mode = peek$(ctx, "adaptiveRender");
572
- return listen$(ctx, "adaptiveRender", (nextMode) => {
573
- if (mode !== nextMode) {
574
- mode = nextMode;
575
- callbackRef.current(nextMode);
576
- }
577
- });
578
- }, [ctx]);
579
- }
580
- function useViewability(callback, configId) {
581
- const ctx = useStateContext();
582
- const containerContext = useContextContainer();
583
- useInit(() => {
584
- if (!containerContext) {
585
- return;
586
- }
587
- const { containerId } = containerContext;
588
- const key = containerId + (configId != null ? configId : "");
589
- const value = ctx.mapViewabilityValues.get(key);
590
- if (value) {
591
- callback(value);
592
- }
593
- });
594
- useEffect(() => {
595
- if (!containerContext) {
596
- return;
597
- }
598
- const { containerId } = containerContext;
599
- const key = containerId + (configId != null ? configId : "");
600
- ctx.mapViewabilityCallbacks.set(key, callback);
601
- return () => {
602
- ctx.mapViewabilityCallbacks.delete(key);
603
- };
604
- }, [ctx, callback, configId, containerContext]);
605
- }
606
- function useViewabilityAmount(callback) {
607
- const ctx = useStateContext();
608
- const containerContext = useContextContainer();
609
- useInit(() => {
610
- if (!containerContext) {
611
- return;
612
- }
613
- const { containerId } = containerContext;
614
- const value = ctx.mapViewabilityAmountValues.get(containerId);
615
- if (value) {
616
- callback(value);
617
- }
618
- });
619
- useEffect(() => {
620
- if (!containerContext) {
621
- return;
622
- }
623
- const { containerId } = containerContext;
624
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
625
- return () => {
626
- ctx.mapViewabilityAmountCallbacks.delete(containerId);
627
- };
628
- }, [ctx, callback, containerContext]);
629
- }
630
- function useRecyclingEffect(effect) {
631
- const containerContext = useContextContainer();
632
- const itemInfo = useContainerItemInfo(containerContext);
633
- const prevInfo = useRef(void 0);
634
- useEffect(() => {
635
- if (!itemInfo) {
636
- return;
637
- }
638
- let ret;
639
- if (prevInfo.current) {
640
- ret = effect({
641
- index: itemInfo.index,
642
- item: itemInfo.value,
643
- prevIndex: prevInfo.current.index,
644
- prevItem: prevInfo.current.value
645
- });
646
- }
647
- prevInfo.current = itemInfo;
648
- return ret;
649
- }, [effect, itemInfo]);
650
- }
651
- function useRecyclingState(valueOrFun) {
652
- var _a3;
653
- const containerContext = useContextContainer();
654
- const itemInfo = useContainerItemInfo(containerContext);
655
- const computeValue = (info) => {
656
- if (isFunction(valueOrFun)) {
657
- const initializer = valueOrFun;
658
- return info ? initializer({
659
- index: info.index,
660
- item: info.value,
661
- prevIndex: void 0,
662
- prevItem: void 0
663
- }) : initializer();
664
- }
665
- return valueOrFun;
666
- };
667
- const [stateValue, setStateValue] = useState(() => {
668
- return computeValue(itemInfo);
669
- });
670
- const prevItemKeyRef = useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
671
- if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
672
- prevItemKeyRef.current = itemInfo.itemKey;
673
- setStateValue(computeValue(itemInfo));
674
- }
675
- const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
676
- const setState = useCallback(
677
- (newState) => {
678
- if (!triggerLayout) {
679
- return;
680
- }
681
- setStateValue((prevValue) => {
682
- return isFunction(newState) ? newState(prevValue) : newState;
683
- });
684
- triggerLayout();
685
- },
686
- [triggerLayout]
687
- );
688
- return [stateValue, setState];
689
- }
690
- function useIsLastItem() {
691
- const containerContext = useContextContainer();
692
- const itemKey = useContainerItemKey(containerContext);
693
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
694
- if (containerContext && !isNullOrUndefined(itemKey)) {
695
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
696
- }
697
- return false;
698
- });
699
- return isLast;
700
- }
701
- function useListScrollSize() {
702
- const [scrollSize] = useArr$(["scrollSize"]);
703
- return scrollSize;
704
- }
705
- var noop = () => {
706
- };
707
- function useSyncLayout() {
708
- const containerContext = useContextContainer();
709
- return containerContext ? containerContext.triggerLayout : noop;
710
- }
711
-
712
- // src/components/Separator.tsx
713
- function Separator({ ItemSeparatorComponent, leadingItem }) {
714
- const isLastItem = useIsLastItem();
715
- return isLastItem ? null : /* @__PURE__ */ React3.createElement(ItemSeparatorComponent, { leadingItem });
716
- }
717
599
 
718
- // src/core/deferredPublicOnScroll.ts
719
- function withResolvedContentOffset(state, event, resolvedOffset) {
720
- return {
721
- ...event,
722
- nativeEvent: {
723
- ...event.nativeEvent,
724
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
725
- }
726
- };
727
- }
728
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
729
- var _a3, _b, _c, _d;
600
+ // src/utils/edgeReachedGate.ts
601
+ function resetEdgeLatch(ctx, edge) {
730
602
  const state = ctx.state;
731
- const deferredEvent = state.deferredPublicOnScrollEvent;
732
- state.deferredPublicOnScrollEvent = void 0;
733
- if (deferredEvent) {
734
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
735
- _c,
736
- withResolvedContentOffset(
737
- state,
738
- deferredEvent,
739
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
740
- )
741
- );
742
- }
743
- }
744
-
745
- // src/core/initialScrollSession.ts
746
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
747
- function hasInitialScrollSessionCompletion(completion) {
748
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
749
- }
750
- function clearInitialScrollSession(state) {
751
- state.initialScrollSession = void 0;
752
- return void 0;
753
- }
754
- function createInitialScrollSession(options) {
755
- const { bootstrap, completion, kind, previousDataLength } = options;
756
- return kind === "offset" ? {
757
- completion,
758
- kind,
759
- previousDataLength
760
- } : {
761
- bootstrap,
762
- completion,
763
- kind,
764
- previousDataLength
765
- };
766
- }
767
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
768
- var _a4, _b2;
769
- if (!state.initialScrollSession) {
770
- state.initialScrollSession = createInitialScrollSession({
771
- completion: {},
772
- kind,
773
- previousDataLength: 0
774
- });
775
- } else if (state.initialScrollSession.kind !== kind) {
776
- state.initialScrollSession = createInitialScrollSession({
777
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
778
- completion: state.initialScrollSession.completion,
779
- kind,
780
- previousDataLength: state.initialScrollSession.previousDataLength
781
- });
782
- }
783
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
784
- return state.initialScrollSession.completion;
785
- }
786
- var initialScrollCompletion = {
787
- didDispatchNativeScroll(state) {
788
- var _a3, _b;
789
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
790
- },
791
- didRetrySilentInitialScroll(state) {
792
- var _a3, _b;
793
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
794
- },
795
- markInitialScrollNativeDispatch(state) {
796
- ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
797
- },
798
- markSilentInitialScrollRetry(state) {
799
- ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
800
- },
801
- resetFlags(state) {
802
- if (!state.initialScrollSession) {
803
- return;
804
- }
805
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
806
- completion.didDispatchNativeScroll = void 0;
807
- completion.didRetrySilentInitialScroll = void 0;
808
- }
809
- };
810
- var initialScrollWatchdog = {
811
- clear(state) {
812
- initialScrollWatchdog.set(state, void 0);
813
- },
814
- didReachTarget(newScroll, watchdog) {
815
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
816
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
817
- },
818
- get(state) {
819
- var _a3, _b;
820
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
821
- },
822
- hasNonZeroTargetOffset(targetOffset) {
823
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
824
- },
825
- isAtZeroTargetOffset(targetOffset) {
826
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
827
- },
828
- set(state, watchdog) {
829
- var _a3, _b;
830
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
831
- return;
832
- }
833
- const completion = ensureInitialScrollSessionCompletion(state);
834
- completion.watchdog = watchdog ? {
835
- startScroll: watchdog.startScroll,
836
- targetOffset: watchdog.targetOffset
837
- } : void 0;
838
- }
839
- };
840
- function setInitialScrollSession(state, options = {}) {
841
- var _a3, _b, _c, _d;
842
- const existingSession = state.initialScrollSession;
843
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
844
- const completion = existingSession == null ? void 0 : existingSession.completion;
845
- const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
846
- const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
847
- if (!kind) {
848
- return clearInitialScrollSession(state);
849
- }
850
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
851
- return clearInitialScrollSession(state);
603
+ if (edge === "start") {
604
+ state.isStartReached = false;
605
+ state.startReachedSnapshot = void 0;
606
+ } else {
607
+ state.isEndReached = false;
608
+ state.endReachedSnapshot = void 0;
852
609
  }
853
- const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
854
- state.initialScrollSession = createInitialScrollSession({
855
- bootstrap,
856
- completion,
857
- kind,
858
- previousDataLength
859
- });
860
- return state.initialScrollSession;
861
610
  }
862
-
863
- // src/utils/checkThreshold.ts
864
- var HYSTERESIS_MULTIPLIER = 1.3;
865
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
866
- const absDistance = Math.abs(distance);
867
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
868
- const updateSnapshot = () => {
869
- setSnapshot({
870
- atThreshold,
871
- contentSize: context.contentSize,
872
- dataLength: context.dataLength,
873
- scrollPosition: context.scrollPosition
874
- });
875
- };
876
- if (!wasReached) {
877
- if (!within) {
878
- return false;
879
- }
880
- onReached(distance);
881
- updateSnapshot();
882
- return true;
883
- }
884
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
885
- if (reset) {
886
- setSnapshot(void 0);
887
- return false;
611
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
612
+ const state = ctx.state;
613
+ if (!state.edgeReachedGate) {
614
+ return;
888
615
  }
889
- if (within) {
890
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
891
- if (changed) {
892
- if (allowReentryOnChange) {
893
- onReached(distance);
894
- }
895
- updateSnapshot();
896
- }
616
+ const contentSize = getContentSize(ctx);
617
+ const endDistance = contentSize - state.scroll - state.scrollLength - getContentInsetEnd(ctx);
618
+ const isContentLess = contentSize < state.scrollLength;
619
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
620
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
621
+ const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
622
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
623
+ if (isOutsideStart && isOutsideEnd) {
624
+ state.edgeReachedGate = void 0;
897
625
  }
898
- return true;
899
- };
626
+ }
627
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
628
+ return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
629
+ }
630
+ function markReachedEdge(ctx) {
631
+ ctx.state.edgeReachedGate = "closed";
632
+ }
633
+ function prepareReachedEdgeForNextUserScroll(ctx) {
634
+ if (ctx.state.edgeReachedGate) {
635
+ ctx.state.edgeReachedGate = "prepared";
636
+ }
637
+ }
638
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
639
+ const state = ctx.state;
640
+ if (state.edgeReachedGate !== "prepared") {
641
+ return void 0;
642
+ }
643
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
644
+ state.edgeReachedGate = "closed";
645
+ resetEdgeLatch(ctx, allowedEdge);
646
+ return allowedEdge;
647
+ }
900
648
 
901
649
  // src/utils/hasActiveInitialScroll.ts
902
650
  function hasActiveInitialScroll(state) {
@@ -904,7 +652,7 @@ function hasActiveInitialScroll(state) {
904
652
  }
905
653
 
906
654
  // src/utils/checkAtBottom.ts
907
- function checkAtBottom(ctx) {
655
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
908
656
  var _a3;
909
657
  const state = ctx.state;
910
658
  if (!state) {
@@ -918,6 +666,7 @@ function checkAtBottom(ctx) {
918
666
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
919
667
  } = state;
920
668
  const contentSize = getContentSize(ctx);
669
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
921
670
  if (contentSize > 0 && queuedInitialLayout) {
922
671
  const insetEnd = getContentInsetEnd(ctx);
923
672
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
@@ -944,72 +693,50 @@ function checkAtBottom(ctx) {
944
693
  },
945
694
  (distance) => {
946
695
  var _a4, _b;
947
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
696
+ if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
697
+ markReachedEdge(ctx);
698
+ (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
699
+ }
948
700
  },
949
701
  (snapshot) => {
950
702
  state.endReachedSnapshot = snapshot;
951
- },
952
- true
703
+ }
953
704
  );
954
705
  }
955
706
  }
956
707
  }
957
708
 
958
- // src/utils/hasActiveMVCPAnchorLock.ts
959
- function hasActiveMVCPAnchorLock(state) {
960
- const lock = state.mvcpAnchorLock;
961
- if (!lock) {
962
- return false;
963
- }
964
- if (Date.now() > lock.expiresAt) {
965
- state.mvcpAnchorLock = void 0;
966
- return false;
967
- }
968
- return true;
969
- }
970
-
971
- // src/utils/isInMVCPActiveMode.ts
972
- function isInMVCPActiveMode(state) {
973
- return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
974
- }
975
-
976
709
  // src/utils/checkAtTop.ts
977
- function checkAtTop(ctx) {
710
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
978
711
  const state = ctx == null ? void 0 : ctx.state;
979
712
  if (!state) {
980
713
  return;
981
714
  }
982
715
  const {
983
- dataChangeEpoch,
984
716
  isStartReached,
985
717
  props: { data, onStartReachedThreshold },
986
718
  scroll,
987
719
  scrollLength,
988
720
  startReachedSnapshot,
989
- startReachedSnapshotDataChangeEpoch,
990
721
  totalSize
991
722
  } = state;
992
723
  const dataLength = data.length;
993
724
  const threshold = onStartReachedThreshold * scrollLength;
994
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
995
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
996
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
997
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
725
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
726
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
998
727
  state.isStartReached = false;
999
728
  state.startReachedSnapshot = void 0;
1000
- state.startReachedSnapshotDataChangeEpoch = void 0;
1001
729
  }
1002
730
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1003
731
  set$(ctx, "isNearStart", scroll <= threshold);
1004
732
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1005
- const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1006
- if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
733
+ if (!shouldSkipThresholdChecks) {
1007
734
  state.isStartReached = checkThreshold(
1008
735
  scroll,
1009
736
  false,
1010
737
  threshold,
1011
738
  state.isStartReached,
1012
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
739
+ startReachedSnapshot,
1013
740
  {
1014
741
  contentSize: totalSize,
1015
742
  dataLength,
@@ -1017,21 +744,23 @@ function checkAtTop(ctx) {
1017
744
  },
1018
745
  (distance) => {
1019
746
  var _a3, _b;
1020
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
747
+ if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
748
+ markReachedEdge(ctx);
749
+ (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
750
+ }
1021
751
  },
1022
752
  (snapshot) => {
1023
753
  state.startReachedSnapshot = snapshot;
1024
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1025
- },
1026
- allowReentryOnDataChange
754
+ }
1027
755
  );
1028
756
  }
1029
757
  }
1030
758
 
1031
759
  // src/utils/checkThresholds.ts
1032
- function checkThresholds(ctx) {
1033
- checkAtBottom(ctx);
1034
- checkAtTop(ctx);
760
+ function checkThresholds(ctx, allowedEdge) {
761
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
762
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
763
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1035
764
  }
1036
765
 
1037
766
  // src/core/recalculateSettledScroll.ts
@@ -1047,13 +776,7 @@ var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1047
776
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1048
777
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1049
778
  function clearAdaptiveRenderExitTimeout(ctx) {
1050
- const state = ctx.state;
1051
- const previousTimeout = state.timeoutAdaptiveRender;
1052
- if (previousTimeout !== void 0) {
1053
- clearTimeout(previousTimeout);
1054
- state.timeouts.delete(previousTimeout);
1055
- state.timeoutAdaptiveRender = void 0;
1056
- }
779
+ ctx.state.scheduledWork.cancel("adaptiveRender");
1057
780
  }
1058
781
  function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1059
782
  const state = ctx.state;
@@ -1061,13 +784,7 @@ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1061
784
  if (exitDelay <= 0) {
1062
785
  setAdaptiveRender(ctx, "normal", "scroll");
1063
786
  } else {
1064
- const timeout = setTimeout(() => {
1065
- state.timeouts.delete(timeout);
1066
- state.timeoutAdaptiveRender = void 0;
1067
- setAdaptiveRender(ctx, "normal", "scroll");
1068
- }, exitDelay);
1069
- state.timeoutAdaptiveRender = timeout;
1070
- state.timeouts.add(timeout);
787
+ state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1071
788
  }
1072
789
  }
1073
790
  function setAdaptiveRender(ctx, mode, reason) {
@@ -1098,7 +815,7 @@ function updateAdaptiveRender(ctx, scrollVelocity, options) {
1098
815
  const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY ;
1099
816
  const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1100
817
  const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1101
- const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
818
+ const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1102
819
  if (nextMode !== previousMode) {
1103
820
  if (nextMode === "light") {
1104
821
  setAdaptiveRender(ctx, "light", "scroll");
@@ -1125,14 +842,13 @@ function getEffectiveDrawDistance(ctx, mode) {
1125
842
  }
1126
843
  function scheduleFullDrawDistancePrewarm(ctx) {
1127
844
  const { state } = ctx;
1128
- if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.queuedFullDrawDistancePrewarm !== void 0) {
845
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1129
846
  return;
1130
847
  }
1131
- state.queuedFullDrawDistancePrewarm = requestAnimationFrame(() => {
848
+ state.scheduledWork.frame(() => {
1132
849
  var _a3;
1133
- state.queuedFullDrawDistancePrewarm = void 0;
1134
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1135
- });
850
+ return (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
851
+ }, "fullDrawDistancePrewarm");
1136
852
  }
1137
853
 
1138
854
  // src/utils/setInitialRenderState.ts
@@ -1190,10 +906,7 @@ function syncInitialScrollOffset(state, offset) {
1190
906
  state.scrollPrev = offset;
1191
907
  }
1192
908
  function clearPreservedInitialScrollTargetTimeout(state) {
1193
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
1194
- clearTimeout(state.timeoutPreservedInitialScrollClear);
1195
- state.timeoutPreservedInitialScrollClear = void 0;
1196
- }
909
+ state.scheduledWork.cancel("preservedInitialScroll");
1197
910
  }
1198
911
  function clearPreservedInitialScrollTarget(state) {
1199
912
  clearPreservedInitialScrollTargetTimeout(state);
@@ -1222,14 +935,17 @@ function finishInitialScroll(ctx, options) {
1222
935
  setInitialScrollSession(state);
1223
936
  clearPreservedInitialScrollTargetTimeout(state);
1224
937
  if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1225
- state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1226
- var _a5;
1227
- state.timeoutPreservedInitialScrollClear = void 0;
1228
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1229
- return;
1230
- }
1231
- clearPreservedInitialScrollTarget(state);
1232
- }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
938
+ state.scheduledWork.timeout(
939
+ () => {
940
+ var _a5;
941
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
942
+ return;
943
+ }
944
+ clearPreservedInitialScrollTarget(state);
945
+ },
946
+ PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
947
+ "preservedInitialScroll"
948
+ );
1233
949
  }
1234
950
  } else {
1235
951
  clearPreservedInitialScrollTarget(state);
@@ -1256,9 +972,11 @@ function calculateOffsetForIndex(ctx, index) {
1256
972
  return index !== void 0 ? state.positions[index] || 0 : 0;
1257
973
  }
1258
974
 
1259
- // src/core/getTopOffsetAdjustment.ts
1260
- function getTopOffsetAdjustment(ctx) {
1261
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
975
+ // src/core/getStartOffsetAdjustment.ts
976
+ function getStartOffsetAdjustment(ctx) {
977
+ const { state } = ctx;
978
+ const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
979
+ return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1262
980
  }
1263
981
 
1264
982
  // src/utils/getId.ts
@@ -1332,6 +1050,50 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1332
1050
  sizes.set(itemKey, size);
1333
1051
  }
1334
1052
 
1053
+ // src/utils/helpers.ts
1054
+ function isFunction(obj) {
1055
+ return typeof obj === "function";
1056
+ }
1057
+ function isArray(obj) {
1058
+ return Array.isArray(obj);
1059
+ }
1060
+ var warned = /* @__PURE__ */ new Set();
1061
+ function warnDevOnce(id, text) {
1062
+ if (IS_DEV && !warned.has(id)) {
1063
+ warned.add(id);
1064
+ console.warn(`[legend-list] ${text}`);
1065
+ }
1066
+ }
1067
+ function roundSize(size) {
1068
+ return Math.floor(size * 8) / 8;
1069
+ }
1070
+ function isNullOrUndefined(value) {
1071
+ return value === null || value === void 0;
1072
+ }
1073
+ function getPadding(s, type) {
1074
+ var _a3, _b, _c;
1075
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1076
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1077
+ }
1078
+ function extractPadding(style, contentContainerStyle, type) {
1079
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1080
+ }
1081
+ function findContainerId(ctx, key) {
1082
+ var _a3, _b;
1083
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1084
+ if (directMatch !== void 0) {
1085
+ return directMatch;
1086
+ }
1087
+ const numContainers = peek$(ctx, "numContainers");
1088
+ for (let i = 0; i < numContainers; i++) {
1089
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1090
+ if (itemKey === key) {
1091
+ return i;
1092
+ }
1093
+ }
1094
+ return -1;
1095
+ }
1096
+
1335
1097
  // src/utils/getItemSize.ts
1336
1098
  function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1337
1099
  var _a3, _b;
@@ -1425,9 +1187,9 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1425
1187
  offset -= viewOffset;
1426
1188
  }
1427
1189
  if (index !== void 0) {
1428
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1429
- if (topOffsetAdjustment) {
1430
- offset += topOffsetAdjustment;
1190
+ const startOffsetAdjustment = getStartOffsetAdjustment(ctx);
1191
+ if (startOffsetAdjustment) {
1192
+ offset += startOffsetAdjustment;
1431
1193
  }
1432
1194
  }
1433
1195
  if (viewPosition !== void 0 && index !== void 0) {
@@ -1437,7 +1199,8 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1437
1199
  }
1438
1200
  const isOutOfBounds = index < 0 || index >= dataLength;
1439
1201
  const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1440
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1202
+ const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1203
+ const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1441
1204
  const trailingInset = getContentInsetEnd(ctx);
1442
1205
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1443
1206
  if (!isOutOfBounds && index === state.props.data.length - 1) {
@@ -1464,11 +1227,35 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1464
1227
  return clampedOffset;
1465
1228
  }
1466
1229
 
1230
+ // src/core/cancelImperativeScroll.ts
1231
+ function cancelScrollCompletionChecks({ scheduledWork }) {
1232
+ scheduledWork.cancel("checkFinishedScrollFrame");
1233
+ scheduledWork.cancel("checkFinishedScrollRetryFrame");
1234
+ scheduledWork.cancel("checkFinishedScrollFallback");
1235
+ scheduledWork.cancel("platformScrollCompletion");
1236
+ }
1237
+ function settlePendingImperativeScroll(state) {
1238
+ var _a3, _b;
1239
+ const resolvePendingScroll = (_b = state.pendingScrollResolve) != null ? _b : (_a3 = state.pendingScrollToEnd) == null ? void 0 : _a3.resolve;
1240
+ state.pendingScrollResolve = void 0;
1241
+ state.pendingScrollToEnd = void 0;
1242
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1243
+ }
1244
+ function cancelImperativeScroll(state) {
1245
+ cancelScrollCompletionChecks(state);
1246
+ state.scheduledWork.cancel("imperativeScrollReady");
1247
+ state.runPendingScrollToEnd = void 0;
1248
+ state.scrollingTo = void 0;
1249
+ state.scrollTargetPinnedRange = void 0;
1250
+ settlePendingImperativeScroll(state);
1251
+ }
1252
+
1467
1253
  // src/core/finishScrollTo.ts
1468
1254
  function finishScrollTo(ctx) {
1469
1255
  var _a3, _b;
1470
1256
  const state = ctx.state;
1471
1257
  if (state == null ? void 0 : state.scrollingTo) {
1258
+ cancelScrollCompletionChecks(state);
1472
1259
  const resolvePendingScroll = state.pendingScrollResolve;
1473
1260
  state.pendingScrollResolve = void 0;
1474
1261
  const scrollingTo = state.scrollingTo;
@@ -1510,6 +1297,7 @@ function doScrollTo(ctx, params) {
1510
1297
  var _a3, _b;
1511
1298
  const state = ctx.state;
1512
1299
  const { animated, horizontal, offset } = params;
1300
+ state.scheduledWork.cancel("platformScrollCompletion");
1513
1301
  const scroller = state.refScroller.current;
1514
1302
  const node = scroller == null ? void 0 : scroller.getScrollableNode();
1515
1303
  if (!scroller || !node) {
@@ -1530,9 +1318,16 @@ function doScrollTo(ctx, params) {
1530
1318
  });
1531
1319
  } else {
1532
1320
  state.scroll = offset;
1533
- setTimeout(() => {
1534
- finishScrollTo(ctx);
1535
- }, 100);
1321
+ const targetToken = state.scrollingTo;
1322
+ state.scheduledWork.timeout(
1323
+ () => {
1324
+ if (targetToken === state.scrollingTo) {
1325
+ finishScrollTo(ctx);
1326
+ }
1327
+ },
1328
+ 100,
1329
+ "platformScrollCompletion"
1330
+ );
1536
1331
  }
1537
1332
  }
1538
1333
  function listenForScrollEnd(ctx, params) {
@@ -1544,23 +1339,28 @@ function listenForScrollEnd(ctx, params) {
1544
1339
  const supportsScrollEnd = "onscrollend" in target;
1545
1340
  let idleTimeout;
1546
1341
  let settled = false;
1547
- const targetToken = ctx.state.scrollingTo;
1342
+ const { scheduledWork, scrollingTo: targetToken } = ctx.state;
1548
1343
  const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1549
1344
  const cleanup = () => {
1550
1345
  target.removeEventListener("scroll", onScroll2);
1551
1346
  if (supportsScrollEnd) {
1552
1347
  target.removeEventListener("scrollend", onScrollEnd);
1553
1348
  }
1554
- if (idleTimeout) {
1349
+ if (idleTimeout !== void 0) {
1555
1350
  clearTimeout(idleTimeout);
1556
1351
  }
1557
1352
  clearTimeout(maxTimeout);
1558
1353
  };
1354
+ const cancel = () => {
1355
+ if (!settled) {
1356
+ settled = true;
1357
+ cleanup();
1358
+ }
1359
+ };
1559
1360
  const finish = (reason) => {
1560
1361
  if (settled) return;
1561
1362
  if (targetToken !== ctx.state.scrollingTo) {
1562
- settled = true;
1563
- cleanup();
1363
+ scheduledWork.cancel("platformScrollCompletion");
1564
1364
  return;
1565
1365
  }
1566
1366
  const currentOffset = readOffset();
@@ -1568,12 +1368,11 @@ function listenForScrollEnd(ctx, params) {
1568
1368
  if (reason === "scrollend" && !isNearTarget) {
1569
1369
  return;
1570
1370
  }
1571
- settled = true;
1572
- cleanup();
1371
+ scheduledWork.cancel("platformScrollCompletion");
1573
1372
  finishScrollTo(ctx);
1574
1373
  };
1575
1374
  const onScroll2 = () => {
1576
- if (idleTimeout) {
1375
+ if (idleTimeout !== void 0) {
1577
1376
  clearTimeout(idleTimeout);
1578
1377
  }
1579
1378
  idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
@@ -1585,6 +1384,7 @@ function listenForScrollEnd(ctx, params) {
1585
1384
  } else {
1586
1385
  idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1587
1386
  }
1387
+ scheduledWork.register("platformScrollCompletion", cancel);
1588
1388
  }
1589
1389
 
1590
1390
  // src/core/doMaintainScrollAtEnd.ts
@@ -1611,9 +1411,12 @@ function doMaintainScrollAtEnd(ctx) {
1611
1411
  if (!state.maintainingScrollAtEnd) {
1612
1412
  const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1613
1413
  const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1414
+ const scrollAtRequest = state.scroll;
1614
1415
  state.maintainingScrollAtEnd = pendingState;
1615
1416
  requestAnimationFrame(() => {
1616
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1417
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1418
+ const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1419
+ if (isStillWithinThreshold || !didScrollSinceRequest) {
1617
1420
  state.maintainingScrollAtEnd = activeState;
1618
1421
  const scroller = refScroller.current;
1619
1422
  if (state.props.horizontal && isHorizontalRTL(state)) {
@@ -1643,6 +1446,7 @@ function doMaintainScrollAtEnd(ctx) {
1643
1446
  );
1644
1447
  } else if (state.maintainingScrollAtEnd === pendingState) {
1645
1448
  state.maintainingScrollAtEnd = void 0;
1449
+ state.pendingMaintainScrollAtEnd = false;
1646
1450
  }
1647
1451
  });
1648
1452
  } else {
@@ -2010,6 +1814,24 @@ var getScrollVelocity = (state) => {
2010
1814
  return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2011
1815
  };
2012
1816
 
1817
+ // src/utils/hasActiveMVCPAnchorLock.ts
1818
+ function hasActiveMVCPAnchorLock(state) {
1819
+ const lock = state.mvcpAnchorLock;
1820
+ if (!lock) {
1821
+ return false;
1822
+ }
1823
+ if (Date.now() > lock.expiresAt) {
1824
+ state.mvcpAnchorLock = void 0;
1825
+ return false;
1826
+ }
1827
+ return true;
1828
+ }
1829
+
1830
+ // src/utils/isInMVCPActiveMode.ts
1831
+ function isInMVCPActiveMode(state) {
1832
+ return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
1833
+ }
1834
+
2013
1835
  // src/core/updateScroll.ts
2014
1836
  function updateScroll(ctx, newScroll, forceUpdate, options) {
2015
1837
  var _a3;
@@ -2047,6 +1869,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2047
1869
  state.scroll = newScroll;
2048
1870
  state.scrollTime = currentTime;
2049
1871
  const scrollDelta = Math.abs(newScroll - prevScroll);
1872
+ const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1873
+ const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
2050
1874
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2051
1875
  const scrollLength = state.scrollLength;
2052
1876
  const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
@@ -2054,7 +1878,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2054
1878
  updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2055
1879
  const lastCalculated = state.scrollLastCalculate;
2056
1880
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2057
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1881
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2058
1882
  if (shouldUpdate) {
2059
1883
  state.scrollLastCalculate = state.scroll;
2060
1884
  state.ignoreScrollFromMVCPIgnored = false;
@@ -2069,16 +1893,13 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2069
1893
  calculateItemsParams.drawDistanceMode = "visible-first";
2070
1894
  }
2071
1895
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2072
- checkThresholds(ctx);
1896
+ checkThresholds(ctx, allowedEdge);
2073
1897
  };
2074
1898
  if (isLargeUserScrollJump) {
2075
1899
  state.mvcpAnchorLock = void 0;
2076
1900
  state.pendingNativeMVCPAdjust = void 0;
2077
1901
  state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
2078
- if (state.queuedMVCPRecalculate !== void 0) {
2079
- cancelAnimationFrame(state.queuedMVCPRecalculate);
2080
- state.queuedMVCPRecalculate = void 0;
2081
- }
1902
+ state.scheduledWork.cancel("mvcpRecalculate");
2082
1903
  flushSync(runCalculateItems);
2083
1904
  scheduleFullDrawDistancePrewarm(ctx);
2084
1905
  } else {
@@ -2223,12 +2044,7 @@ function scrollTo(ctx, params) {
2223
2044
  const {
2224
2045
  props: { horizontal }
2225
2046
  } = state;
2226
- if (state.animFrameCheckFinishedScroll) {
2227
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2228
- }
2229
- if (state.timeoutCheckFinishedScrollFallback) {
2230
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2231
- }
2047
+ cancelScrollCompletionChecks(state);
2232
2048
  const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2233
2049
  const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2234
2050
  const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
@@ -2333,10 +2149,7 @@ function setInitialScrollTarget(ctx, target, options) {
2333
2149
  var _a3;
2334
2150
  const { state } = ctx;
2335
2151
  state.clearPreservedInitialScrollOnNextFinish = void 0;
2336
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
2337
- clearTimeout(state.timeoutPreservedInitialScrollClear);
2338
- state.timeoutPreservedInitialScrollClear = void 0;
2339
- }
2152
+ state.scheduledWork.cancel("preservedInitialScroll");
2340
2153
  state.initialScroll = target;
2341
2154
  if (options == null ? void 0 : options.resetDidFinish) {
2342
2155
  resetInitialRenderState(ctx, { resetInitialScroll: true });
@@ -2629,13 +2442,13 @@ function rearmBootstrapInitialScroll(ctx, options) {
2629
2442
  queueBootstrapInitialScrollReevaluation(ctx.state);
2630
2443
  }
2631
2444
  function createInitialScrollAtEndTarget(options) {
2632
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2445
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingEnd } = options;
2633
2446
  return {
2634
2447
  contentOffset: void 0,
2635
2448
  index: Math.max(0, dataLength - 1),
2636
2449
  preserveForBottomPadding: true,
2637
2450
  preserveForFooterLayout,
2638
- viewOffset: -stylePaddingBottom - footerSize,
2451
+ viewOffset: -stylePaddingEnd - footerSize,
2639
2452
  viewPosition: 1
2640
2453
  };
2641
2454
  }
@@ -2649,7 +2462,7 @@ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2649
2462
  return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2650
2463
  }
2651
2464
  function createRetargetedBottomAlignedInitialScroll(options) {
2652
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2465
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd, target } = options;
2653
2466
  const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2654
2467
  return {
2655
2468
  ...target,
@@ -2657,7 +2470,7 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2657
2470
  index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2658
2471
  preserveForBottomPadding: true,
2659
2472
  preserveForFooterLayout,
2660
- viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2473
+ viewOffset: -stylePaddingEnd - (preserveForFooterLayout ? footerSize : 0),
2661
2474
  viewPosition: 1
2662
2475
  };
2663
2476
  }
@@ -2665,7 +2478,7 @@ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2665
2478
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2666
2479
  }
2667
2480
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2668
- const { dataLength, stylePaddingBottom, target } = options;
2481
+ const { dataLength, stylePaddingEnd, target } = options;
2669
2482
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2670
2483
  return;
2671
2484
  }
@@ -2673,7 +2486,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2673
2486
  dataLength,
2674
2487
  footerSize: 0,
2675
2488
  preserveForFooterLayout: void 0,
2676
- stylePaddingBottom
2489
+ stylePaddingEnd
2677
2490
  });
2678
2491
  setInitialScrollTarget(ctx, clearedFooterTarget);
2679
2492
  }
@@ -2734,7 +2547,7 @@ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2734
2547
  });
2735
2548
  }
2736
2549
  function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2737
- var _a3, _b;
2550
+ var _a3;
2738
2551
  const state = ctx.state;
2739
2552
  const initialScroll = state.initialScroll;
2740
2553
  if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
@@ -2746,7 +2559,7 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2746
2559
  if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2747
2560
  clearPendingInitialScrollFooterLayout(ctx, {
2748
2561
  dataLength: state.props.data.length,
2749
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2562
+ stylePaddingEnd: getStylePaddingEnd(state.props),
2750
2563
  target: initialScroll
2751
2564
  });
2752
2565
  } else {
@@ -2783,7 +2596,7 @@ function startBootstrapInitialScrollOnMount(ctx, options) {
2783
2596
  }
2784
2597
  }
2785
2598
  function handleBootstrapInitialScrollDataChange(ctx, options) {
2786
- const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2599
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingEnd } = options;
2787
2600
  const state = ctx.state;
2788
2601
  const initialScroll = state.initialScroll;
2789
2602
  if (isOffsetInitialScrollSession(state) || !initialScroll) {
@@ -2805,18 +2618,18 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2805
2618
  dataLength,
2806
2619
  footerSize: peek$(ctx, "footerSize") || 0,
2807
2620
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2808
- stylePaddingBottom
2621
+ stylePaddingEnd
2809
2622
  }) : createRetargetedBottomAlignedInitialScroll({
2810
2623
  dataLength,
2811
2624
  footerSize: peek$(ctx, "footerSize") || 0,
2812
2625
  initialScrollAtEnd,
2813
- stylePaddingBottom,
2626
+ stylePaddingEnd,
2814
2627
  target: initialScroll
2815
2628
  });
2816
2629
  if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2817
2630
  clearPendingInitialScrollFooterLayout(ctx, {
2818
2631
  dataLength,
2819
- stylePaddingBottom,
2632
+ stylePaddingEnd,
2820
2633
  target: initialScroll
2821
2634
  });
2822
2635
  return;
@@ -2848,7 +2661,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2848
2661
  }
2849
2662
  }
2850
2663
  function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2851
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2664
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd } = options;
2852
2665
  const state = ctx.state;
2853
2666
  if (!initialScrollAtEnd) {
2854
2667
  return;
@@ -2864,7 +2677,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2864
2677
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2865
2678
  clearPendingInitialScrollFooterLayout(ctx, {
2866
2679
  dataLength,
2867
- stylePaddingBottom,
2680
+ stylePaddingEnd,
2868
2681
  target: initialScroll
2869
2682
  });
2870
2683
  } else {
@@ -2872,13 +2685,13 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2872
2685
  dataLength,
2873
2686
  footerSize,
2874
2687
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2875
- stylePaddingBottom
2688
+ stylePaddingEnd
2876
2689
  });
2877
2690
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2878
2691
  if (!didTargetChange) {
2879
2692
  clearPendingInitialScrollFooterLayout(ctx, {
2880
2693
  dataLength,
2881
- stylePaddingBottom,
2694
+ stylePaddingEnd,
2882
2695
  target: initialScroll
2883
2696
  });
2884
2697
  } else {
@@ -3030,6 +2843,47 @@ function abortBootstrapInitialScroll(ctx) {
3030
2843
  }
3031
2844
  }
3032
2845
 
2846
+ // src/core/containerItemMetadata.ts
2847
+ function createContainerItemMetadata(state, itemIndex, itemData, itemType) {
2848
+ return {
2849
+ dataChangeEpoch: state.dataChangeEpoch,
2850
+ getFixedItemSize: state.props.getFixedItemSize,
2851
+ getItemType: state.props.getItemType,
2852
+ itemData,
2853
+ itemIndex,
2854
+ itemType
2855
+ };
2856
+ }
2857
+ function resolveContainerItemMetadata(state, containerId, itemIndex, itemData) {
2858
+ var _a3, _b;
2859
+ const { getFixedItemSize, getItemType } = state.props;
2860
+ const previousMetadata = state.containerItemMetadata.get(containerId);
2861
+ let metadata;
2862
+ if ((previousMetadata == null ? void 0 : previousMetadata.dataChangeEpoch) === state.dataChangeEpoch && previousMetadata.getItemType === getItemType && previousMetadata.itemData === itemData && previousMetadata.itemIndex === itemIndex) {
2863
+ metadata = previousMetadata;
2864
+ } else {
2865
+ const itemType = getItemType ? (_a3 = getItemType(itemData, itemIndex)) != null ? _a3 : "" : void 0;
2866
+ metadata = createContainerItemMetadata(state, itemIndex, itemData, itemType);
2867
+ state.containerItemMetadata.set(containerId, metadata);
2868
+ }
2869
+ if (metadata.getFixedItemSize !== getFixedItemSize) {
2870
+ metadata.didResolveFixedItemSize = false;
2871
+ metadata.fixedItemSize = void 0;
2872
+ metadata.getFixedItemSize = getFixedItemSize;
2873
+ }
2874
+ if (getFixedItemSize && !metadata.didResolveFixedItemSize) {
2875
+ metadata.fixedItemSize = getFixedItemSize(itemData, itemIndex, (_b = metadata.itemType) != null ? _b : "");
2876
+ metadata.didResolveFixedItemSize = true;
2877
+ }
2878
+ return metadata;
2879
+ }
2880
+ function invalidateContainerFixedItemSizes(state) {
2881
+ for (const metadata of state.containerItemMetadata.values()) {
2882
+ metadata.didResolveFixedItemSize = false;
2883
+ metadata.fixedItemSize = void 0;
2884
+ }
2885
+ }
2886
+
3033
2887
  // src/core/checkFinishedScroll.ts
3034
2888
  var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3035
2889
  var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
@@ -3045,7 +2899,7 @@ function checkFinishedScroll(ctx, options) {
3045
2899
  return;
3046
2900
  }
3047
2901
  }
3048
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2902
+ ctx.state.scheduledWork.frame(() => checkFinishedScrollFrame(ctx), "checkFinishedScrollFrame");
3049
2903
  }
3050
2904
  function hasScrollCompletionOwnership(state, options) {
3051
2905
  const { clampedTargetOffset, scrollingTo } = options;
@@ -3115,7 +2969,6 @@ function checkFinishedScrollFrame(ctx) {
3115
2969
  return;
3116
2970
  }
3117
2971
  const { state } = ctx;
3118
- state.animFrameCheckFinishedScroll = void 0;
3119
2972
  const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
3120
2973
  if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
3121
2974
  clampedTargetOffset: completionState.clampedTargetOffset,
@@ -3134,47 +2987,47 @@ function scrollToFallbackOffset(ctx, offset) {
3134
2987
  }
3135
2988
  function checkFinishedScrollFallback(ctx) {
3136
2989
  const state = ctx.state;
2990
+ if (state.scheduledWork.has("checkFinishedScrollFallback")) {
2991
+ return;
2992
+ }
3137
2993
  const scrollingTo = state.scrollingTo;
3138
2994
  const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3139
2995
  const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
3140
2996
  const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
3141
2997
  const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
3142
2998
  const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
3143
- state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
3144
- let numChecks = 0;
3145
- const scheduleFallbackCheck = (delay) => {
3146
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
3147
- };
3148
- const checkHasScrolled = () => {
3149
- var _c, _d;
3150
- state.timeoutCheckFinishedScrollFallback = void 0;
3151
- const isStillScrollingTo = state.scrollingTo;
3152
- if (isStillScrollingTo) {
3153
- numChecks++;
3154
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3155
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3156
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3157
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
3158
- state,
3159
- isStillScrollingTo
3160
- );
3161
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3162
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
3163
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
3164
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
3165
- if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3166
- finishScrollTo(ctx);
3167
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
3168
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
3169
- scrollToFallbackOffset(ctx, targetOffset);
3170
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3171
- } else {
3172
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3173
- }
2999
+ let numChecks = 0;
3000
+ const scheduleFallbackCheck = (delay) => {
3001
+ state.scheduledWork.timeout(checkHasScrolled, delay, "checkFinishedScrollFallback");
3002
+ };
3003
+ const checkHasScrolled = () => {
3004
+ var _c, _d;
3005
+ const isStillScrollingTo = state.scrollingTo;
3006
+ if (isStillScrollingTo) {
3007
+ numChecks++;
3008
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3009
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3010
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3011
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
3012
+ state,
3013
+ isStillScrollingTo
3014
+ );
3015
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3016
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
3017
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
3018
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
3019
+ if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3020
+ finishScrollTo(ctx);
3021
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
3022
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
3023
+ scrollToFallbackOffset(ctx, targetOffset);
3024
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3025
+ } else {
3026
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3174
3027
  }
3175
- };
3176
- checkHasScrolled();
3177
- }, initialDelay);
3028
+ }
3029
+ };
3030
+ scheduleFallbackCheck(initialDelay);
3178
3031
  }
3179
3032
 
3180
3033
  // src/core/initialScrollLifecycle.ts
@@ -3250,7 +3103,7 @@ function handleInitialScrollDataChange(ctx, options) {
3250
3103
  initialScrollAtEnd,
3251
3104
  latestInitialScroll,
3252
3105
  latestInitialScrollSessionKind,
3253
- stylePaddingBottom,
3106
+ stylePaddingEnd,
3254
3107
  useBootstrapInitialScroll
3255
3108
  } = options;
3256
3109
  const state = ctx.state;
@@ -3280,7 +3133,7 @@ function handleInitialScrollDataChange(ctx, options) {
3280
3133
  didDataChange,
3281
3134
  initialScrollAtEnd,
3282
3135
  previousDataLength: previousInitialScrollDataLength,
3283
- stylePaddingBottom
3136
+ stylePaddingEnd
3284
3137
  });
3285
3138
  return;
3286
3139
  }
@@ -3303,6 +3156,52 @@ function resetLayoutCachesForDataChange(state) {
3303
3156
  state.columnSpans.length = 0;
3304
3157
  }
3305
3158
 
3159
+ // src/core/scheduleContainerLayout.ts
3160
+ function getContainerLayoutEffectScope(ctx) {
3161
+ var _a3;
3162
+ const scheduledIds = ctx.pendingContainerIds;
3163
+ ctx.pendingContainerIds = void 0;
3164
+ if (scheduledIds === void 0) {
3165
+ return void 0;
3166
+ }
3167
+ const state = ctx.state;
3168
+ let targetContainerIds = scheduledIds;
3169
+ if (targetContainerIds && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size)) {
3170
+ targetContainerIds = new Set(targetContainerIds);
3171
+ for (const itemKey of state.userScrollAnchorReset.keys) {
3172
+ const containerId = state.containerItemKeys.get(itemKey);
3173
+ if (containerId !== void 0) {
3174
+ targetContainerIds.add(containerId);
3175
+ }
3176
+ }
3177
+ }
3178
+ return targetContainerIds;
3179
+ }
3180
+ function scheduleContainerLayout(ctx, target) {
3181
+ var _a3;
3182
+ const isAlreadyScheduled = ctx.pendingContainerIds !== void 0;
3183
+ const previousIds = ctx.pendingContainerIds;
3184
+ if (target === void 0) {
3185
+ ctx.pendingContainerIds = null;
3186
+ } else if (previousIds !== null) {
3187
+ let nextIds = previousIds;
3188
+ if (!nextIds) {
3189
+ nextIds = typeof target === "number" ? /* @__PURE__ */ new Set([target]) : new Set(target);
3190
+ } else if (typeof target === "number") {
3191
+ nextIds.add(target);
3192
+ } else {
3193
+ for (const containerId of target) {
3194
+ nextIds.add(containerId);
3195
+ }
3196
+ }
3197
+ ctx.pendingContainerIds = nextIds;
3198
+ }
3199
+ if (!isAlreadyScheduled) {
3200
+ const nextEpoch = ((_a3 = peek$(ctx, "containerLayoutEpoch")) != null ? _a3 : 0) + 1;
3201
+ set$(ctx, "containerLayoutEpoch", nextEpoch);
3202
+ }
3203
+ }
3204
+
3306
3205
  // src/core/syncMountedContainer.ts
3307
3206
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3308
3207
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
@@ -3346,14 +3245,13 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3346
3245
  set$(ctx, `containerSpan${containerIndex}`, span);
3347
3246
  }
3348
3247
  }
3349
- const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3248
+ const prevIndex = peek$(ctx, `containerItemIndex${containerIndex}`);
3249
+ if (prevIndex !== itemIndex) {
3250
+ set$(ctx, `containerItemIndex${containerIndex}`, itemIndex);
3251
+ }
3350
3252
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3351
- let itemInfoValue = prevData;
3352
- let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3353
3253
  const updateData = () => {
3354
3254
  set$(ctx, `containerItemData${containerIndex}`, item);
3355
- itemInfoValue = item;
3356
- didChangeItemInfo = true;
3357
3255
  didRefreshData = true;
3358
3256
  };
3359
3257
  if (prevData !== item) {
@@ -3388,13 +3286,6 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3388
3286
  }
3389
3287
  }
3390
3288
  }
3391
- if (didChangeItemInfo) {
3392
- set$(ctx, `containerItemInfo${containerIndex}`, {
3393
- index: itemIndex,
3394
- itemKey,
3395
- value: itemInfoValue
3396
- });
3397
- }
3398
3289
  return { didChangePosition, didRefreshData };
3399
3290
  }
3400
3291
 
@@ -3712,7 +3603,6 @@ function setupViewability(props) {
3712
3603
  }
3713
3604
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3714
3605
  const {
3715
- timeouts,
3716
3606
  props: { data }
3717
3607
  } = state;
3718
3608
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
@@ -3722,11 +3612,10 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3722
3612
  viewabilityState.startBuffered = startBuffered;
3723
3613
  viewabilityState.endBuffered = endBuffered;
3724
3614
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3725
- const timer = setTimeout(() => {
3726
- timeouts.delete(timer);
3727
- updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3728
- }, viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime);
3729
- timeouts.add(timer);
3615
+ state.scheduledWork.timeout(
3616
+ () => updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize),
3617
+ viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime
3618
+ );
3730
3619
  } else {
3731
3620
  updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3732
3621
  }
@@ -3759,19 +3648,29 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3759
3648
  if (previousViewableItems) {
3760
3649
  for (const viewToken of previousViewableItems) {
3761
3650
  previousViewableKeys.add(viewToken.key);
3651
+ const currentIndex = state.indexByKey.get(viewToken.key);
3652
+ const currentItem = currentIndex !== void 0 ? data[currentIndex] : void 0;
3762
3653
  const containerId = findContainerId(ctx, viewToken.key);
3763
- if (!checkIsViewable(
3764
- state,
3765
- ctx,
3766
- viewabilityConfig,
3767
- containerId,
3768
- viewToken.key,
3769
- scrollSize,
3770
- viewToken.item,
3771
- viewToken.index
3772
- )) {
3773
- viewToken.isViewable = false;
3774
- changed.push(viewToken);
3654
+ let isStillViewable = false;
3655
+ if (currentIndex !== void 0 && currentItem !== void 0) {
3656
+ isStillViewable = checkIsViewable(
3657
+ state,
3658
+ ctx,
3659
+ viewabilityConfig,
3660
+ containerId,
3661
+ viewToken.key,
3662
+ scrollSize,
3663
+ currentItem,
3664
+ currentIndex
3665
+ );
3666
+ }
3667
+ if (!isStillViewable) {
3668
+ changed.push({
3669
+ ...viewToken,
3670
+ index: currentIndex != null ? currentIndex : viewToken.index,
3671
+ isViewable: false,
3672
+ item: currentItem != null ? currentItem : viewToken.item
3673
+ });
3775
3674
  }
3776
3675
  }
3777
3676
  }
@@ -3928,128 +3827,102 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3928
3827
 
3929
3828
  // src/utils/findAvailableContainers.ts
3930
3829
  function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
3830
+ var _a3;
3931
3831
  const numNeeded = needNewContainers.length;
3932
3832
  if (numNeeded === 0) {
3933
3833
  return [];
3934
3834
  }
3935
3835
  const numContainers = peek$(ctx, "numContainers");
3936
3836
  const state = ctx.state;
3937
- const { stickyContainerPool, containerItemTypes } = state;
3837
+ const { containerItemMetadata, stickyContainerPool } = state;
3938
3838
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3939
- const allocations = [];
3940
3839
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
3941
- let pendingRemovalChanged = false;
3840
+ const requests = needNewContainers.map((itemIndex, order) => ({
3841
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3842
+ itemIndex,
3843
+ itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3844
+ order
3845
+ }));
3846
+ const normalRequests = requests.filter((request) => !request.isSticky);
3847
+ const stickyRequests = requests.filter((request) => request.isSticky);
3848
+ const normalCandidates = [];
3849
+ const stickyCandidates = [];
3850
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
3851
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
3852
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3853
+ const isProtected = !!key && !!(protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key);
3854
+ if (isProtected) {
3855
+ continue;
3856
+ }
3857
+ if (stickyContainerPool.has(containerIndex)) {
3858
+ if (key === void 0 || isPendingRemoval) {
3859
+ stickyCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3860
+ }
3861
+ } else if (key === void 0 || isPendingRemoval) {
3862
+ normalCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3863
+ } else if (!shouldAvoidAssignedContainerReuse) {
3864
+ const index = state.indexByKey.get(key);
3865
+ if (index !== void 0 && (index < startBuffered || index > endBuffered)) {
3866
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3867
+ normalCandidates.push({ containerIndex, distance });
3868
+ }
3869
+ }
3870
+ }
3871
+ normalCandidates.sort(comparatorByDistance);
3872
+ const allocations = new Array(numNeeded);
3942
3873
  let nextNewContainerIndex = numContainers;
3943
- const usedContainers = /* @__PURE__ */ new Set();
3944
- let availableContainers;
3945
- const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
3946
- const canReuseContainer = (containerIndex, requiredType) => {
3947
- if (!requiredType) return true;
3948
- const existingType = containerItemTypes.get(containerIndex);
3949
- if (!existingType) return true;
3950
- return existingType === requiredType;
3951
- };
3952
- const pushAllocation = (itemIndex, itemType, containerIndex) => {
3953
- allocations.push({
3874
+ let pendingRemovalChanged = false;
3875
+ const assign = (request, containerIndex) => {
3876
+ allocations[request.order] = {
3954
3877
  containerIndex,
3955
- itemIndex,
3956
- itemType
3957
- });
3958
- usedContainers.add(containerIndex);
3878
+ itemIndex: request.itemIndex,
3879
+ itemType: request.itemType
3880
+ };
3959
3881
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
3960
3882
  pendingRemovalChanged = true;
3961
3883
  }
3962
3884
  };
3963
- const pushNewContainer = (itemIndex, itemType, isSticky) => {
3964
- const newContainerIndex = nextNewContainerIndex++;
3965
- pushAllocation(itemIndex, itemType, newContainerIndex);
3966
- if (isSticky) {
3967
- stickyContainerPool.add(newContainerIndex);
3968
- }
3969
- return newContainerIndex;
3970
- };
3971
- const canUseContainer = (containerIndex, itemType) => {
3972
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
3973
- return false;
3974
- }
3975
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
3976
- const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3977
- return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
3978
- };
3979
- const findStickyContainer = (itemType) => {
3980
- let foundContainer;
3981
- for (const containerIndex of stickyContainerPool) {
3982
- if (!usedContainers.has(containerIndex)) {
3983
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
3984
- const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3985
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
3986
- foundContainer = containerIndex;
3987
- break;
3988
- }
3989
- }
3990
- }
3991
- return foundContainer;
3992
- };
3993
- const findUnassignedOrPendingContainer = (itemType) => {
3994
- let foundContainer;
3995
- for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
3996
- if (canUseContainer(containerIndex, itemType)) {
3997
- foundContainer = containerIndex;
3885
+ const assignMatching = (pendingRequests, candidates, matches) => {
3886
+ for (const request of pendingRequests) {
3887
+ if (allocations[request.order]) {
3888
+ continue;
3998
3889
  }
3999
- }
4000
- return foundContainer;
4001
- };
4002
- const getAvailableContainers = () => {
4003
- if (!availableContainers) {
4004
- availableContainers = [];
4005
- if (!shouldAvoidAssignedContainerReuse) {
4006
- for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4007
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4008
- continue;
4009
- }
4010
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4011
- if (key === void 0) continue;
4012
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4013
- const index = state.indexByKey.get(key);
4014
- const isOutOfView = index < startBuffered || index > endBuffered;
4015
- if (isOutOfView) {
4016
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4017
- availableContainers.push({ distance, index: containerIndex });
4018
- }
3890
+ const candidateIndex = candidates.findIndex(
3891
+ (candidate) => {
3892
+ var _a4;
3893
+ return matches((_a4 = containerItemMetadata.get(candidate.containerIndex)) == null ? void 0 : _a4.itemType, request.itemType);
4019
3894
  }
4020
- availableContainers.sort(comparatorByDistance);
3895
+ );
3896
+ if (candidateIndex !== -1) {
3897
+ const [candidate] = candidates.splice(candidateIndex, 1);
3898
+ assign(request, candidate.containerIndex);
4021
3899
  }
4022
3900
  }
4023
- return availableContainers;
4024
3901
  };
4025
- const findAvailableContainer = (itemType) => {
4026
- const containers = getAvailableContainers();
4027
- let matchIndex = -1;
4028
- for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4029
- const containerIndex = containers[i].index;
4030
- if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4031
- matchIndex = i;
4032
- }
3902
+ const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
3903
+ if (getRequiredItemType) {
3904
+ assignMatching(
3905
+ pendingRequests,
3906
+ candidates,
3907
+ (containerType, requestType) => requestType !== void 0 && containerType === requestType
3908
+ );
3909
+ }
3910
+ assignMatching(pendingRequests, candidates, (containerType) => containerType === void 0);
3911
+ if (allowCrossType) {
3912
+ assignMatching(pendingRequests, candidates, () => true);
4033
3913
  }
4034
- return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4035
3914
  };
4036
- for (const itemIndex of needNewContainers) {
4037
- const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4038
- const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4039
- let containerIndex;
4040
- if (isSticky) {
4041
- containerIndex = findStickyContainer(itemType);
4042
- } else {
4043
- containerIndex = findUnassignedOrPendingContainer(itemType);
4044
- if (containerIndex === void 0) {
4045
- containerIndex = findAvailableContainer(itemType);
4046
- }
3915
+ assignFromPool(normalRequests, normalCandidates, true);
3916
+ assignFromPool(stickyRequests, stickyCandidates, false);
3917
+ for (const request of requests) {
3918
+ if (allocations[request.order]) {
3919
+ continue;
4047
3920
  }
4048
- if (containerIndex !== void 0) {
4049
- pushAllocation(itemIndex, itemType, containerIndex);
4050
- } else {
4051
- pushNewContainer(itemIndex, itemType, isSticky);
3921
+ const containerIndex = nextNewContainerIndex++;
3922
+ if (request.isSticky) {
3923
+ stickyContainerPool.add(containerIndex);
4052
3924
  }
3925
+ assign(request, containerIndex);
4053
3926
  }
4054
3927
  if (pendingRemovalChanged) {
4055
3928
  pendingRemoval.length = 0;
@@ -4059,18 +3932,21 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4059
3932
  }
4060
3933
  }
4061
3934
  }
4062
- if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4063
- console.warn(
4064
- "[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize.",
4065
- {
4066
- debugInfo: {
4067
- numContainers,
4068
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4069
- numNeeded,
4070
- stillNeeded: nextNewContainerIndex - numContainers
3935
+ if (IS_DEV) {
3936
+ const numContainersPooled = (_a3 = peek$(ctx, "numContainersPooled")) != null ? _a3 : Number.POSITIVE_INFINITY;
3937
+ if (nextNewContainerIndex > numContainersPooled) {
3938
+ console.warn(
3939
+ "[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.",
3940
+ {
3941
+ debugInfo: {
3942
+ numContainers,
3943
+ numContainersPooled,
3944
+ numNeeded,
3945
+ stillNeeded: nextNewContainerIndex - numContainers
3946
+ }
4071
3947
  }
4072
- }
4073
- );
3948
+ );
3949
+ }
4074
3950
  }
4075
3951
  return allocations;
4076
3952
  }
@@ -4098,20 +3974,15 @@ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4098
3974
  }
4099
3975
  function scheduleRenderRangeProjectionSettle(ctx) {
4100
3976
  const state = ctx.state;
4101
- const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4102
- if (previousTimeout !== void 0) {
4103
- clearTimeout(previousTimeout);
4104
- state.timeouts.delete(previousTimeout);
4105
- }
4106
- const timeout = setTimeout(() => {
4107
- var _a3;
4108
- state.timeoutRenderRangeProjectionSettle = void 0;
4109
- state.timeouts.delete(timeout);
4110
- state.scrollHistory.length = 0;
4111
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4112
- }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4113
- state.timeoutRenderRangeProjectionSettle = timeout;
4114
- state.timeouts.add(timeout);
3977
+ state.scheduledWork.timeout(
3978
+ () => {
3979
+ var _a3;
3980
+ state.scrollHistory.length = 0;
3981
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
3982
+ },
3983
+ RENDER_RANGE_PROJECTION_SETTLE_DELAY,
3984
+ "renderRangeProjection"
3985
+ );
4115
3986
  }
4116
3987
  function findCurrentStickyIndex(stickyArray, scroll, state) {
4117
3988
  const positions = state.positions;
@@ -4305,7 +4176,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4305
4176
  function calculateItemsInView(ctx, params = {}) {
4306
4177
  const state = ctx.state;
4307
4178
  batchedUpdates(() => {
4308
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4179
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4309
4180
  const {
4310
4181
  columns,
4311
4182
  containerItemKeys,
@@ -4333,6 +4204,7 @@ function calculateItemsInView(ctx, params = {}) {
4333
4204
  return;
4334
4205
  }
4335
4206
  let totalSize = getContentSize(ctx);
4207
+ let changedContainerIds;
4336
4208
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4337
4209
  const numColumns = peek$(ctx, "numColumns");
4338
4210
  const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
@@ -4458,7 +4330,7 @@ function calculateItemsInView(ctx, params = {}) {
4458
4330
  const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4459
4331
  checkMVCP == null ? void 0 : checkMVCP();
4460
4332
  const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4461
- if (didMVCPAdjustScroll && (initialScroll || state.scrollingTo)) {
4333
+ if (didMVCPAdjustScroll) {
4462
4334
  updateScroll2(state.scroll);
4463
4335
  updateScrollRange();
4464
4336
  }
@@ -4638,22 +4510,20 @@ function calculateItemsInView(ctx, params = {}) {
4638
4510
  if (oldKey && oldKey !== id) {
4639
4511
  containerItemKeys.delete(oldKey);
4640
4512
  }
4513
+ if (oldKey !== id) {
4514
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4515
+ changedContainerIds.add(containerIndex);
4516
+ state.containerItemGenerations[containerIndex] = ((_p = state.containerItemGenerations[containerIndex]) != null ? _p : 0) + 1;
4517
+ }
4641
4518
  set$(ctx, `containerItemKey${containerIndex}`, id);
4519
+ set$(ctx, `containerItemIndex${containerIndex}`, i);
4642
4520
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4643
- set$(ctx, `containerItemInfo${containerIndex}`, {
4644
- index: i,
4645
- itemKey: id,
4646
- value: data[i]
4647
- });
4648
- if (allocation.itemType !== void 0) {
4649
- state.containerItemTypes.set(containerIndex, allocation.itemType);
4650
- }
4521
+ state.containerItemMetadata.set(
4522
+ containerIndex,
4523
+ createContainerItemMetadata(state, i, data[i], allocation.itemType)
4524
+ );
4651
4525
  containerItemKeys.set(id, containerIndex);
4652
- (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4653
- {
4654
- (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4655
- state.pendingLayoutEffectMeasurements.add(id);
4656
- }
4526
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4657
4527
  const containerSticky = `containerSticky${containerIndex}`;
4658
4528
  const isSticky = stickyHeaderIndicesSet.has(i);
4659
4529
  const isPinnedRender = isPinnedRenderIndex(i);
@@ -4703,15 +4573,18 @@ function calculateItemsInView(ctx, params = {}) {
4703
4573
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4704
4574
  if (itemKey !== void 0) {
4705
4575
  containerItemKeys.delete(itemKey);
4576
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4577
+ changedContainerIds.add(i);
4578
+ state.containerItemGenerations[i] = ((_t = state.containerItemGenerations[i]) != null ? _t : 0) + 1;
4706
4579
  }
4707
- state.containerItemTypes.delete(i);
4580
+ state.containerItemMetadata.delete(i);
4708
4581
  if (state.stickyContainerPool.has(i)) {
4709
4582
  set$(ctx, `containerSticky${i}`, false);
4710
4583
  state.stickyContainerPool.delete(i);
4711
4584
  }
4712
4585
  set$(ctx, `containerItemKey${i}`, void 0);
4586
+ set$(ctx, `containerItemIndex${i}`, void 0);
4713
4587
  set$(ctx, `containerItemData${i}`, void 0);
4714
- set$(ctx, `containerItemInfo${i}`, void 0);
4715
4588
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4716
4589
  set$(ctx, `containerColumn${i}`, -1);
4717
4590
  set$(ctx, `containerSpan${i}`, 1);
@@ -4725,6 +4598,9 @@ function calculateItemsInView(ctx, params = {}) {
4725
4598
  }
4726
4599
  }
4727
4600
  }
4601
+ if (changedContainerIds && (IsNewArchitecture)) {
4602
+ scheduleContainerLayout(ctx, changedContainerIds);
4603
+ }
4728
4604
  if (didChangePositions) {
4729
4605
  set$(ctx, "lastPositionUpdate", Date.now());
4730
4606
  }
@@ -4741,20 +4617,18 @@ function calculateItemsInView(ctx, params = {}) {
4741
4617
  }
4742
4618
  }
4743
4619
  if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4744
- if (!didMVCPAdjustScroll) {
4745
- updateViewableItems(
4746
- ctx.state,
4747
- ctx,
4748
- viewabilityConfigCallbackPairs,
4749
- scrollLength,
4750
- visibleRange.startNoBuffer,
4751
- visibleRange.endNoBuffer,
4752
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4753
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4754
- );
4755
- }
4620
+ updateViewableItems(
4621
+ ctx.state,
4622
+ ctx,
4623
+ viewabilityConfigCallbackPairs,
4624
+ scrollLength,
4625
+ visibleRange.startNoBuffer,
4626
+ visibleRange.endNoBuffer,
4627
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4628
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4629
+ );
4756
4630
  }
4757
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
4631
+ (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4758
4632
  });
4759
4633
  }
4760
4634
 
@@ -4776,8 +4650,6 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4776
4650
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4777
4651
  nextAnchorKey = getId(state, anchorIndex);
4778
4652
  let contentBelowAnchor = 0;
4779
- const footerSize = ctx.values.get("footerSize") || 0;
4780
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4781
4653
  let hasUnknownTailSize = false;
4782
4654
  for (let index = anchorIndex; index < data.length; index++) {
4783
4655
  const size = getKnownOrFixedItemSize(ctx, index);
@@ -4789,14 +4661,16 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4789
4661
  contentBelowAnchor += effectiveSize;
4790
4662
  }
4791
4663
  }
4792
- contentBelowAnchor += footerSize + stylePaddingBottom;
4664
+ contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
4665
+ contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
4793
4666
  isReady = !hasUnknownTailSize;
4794
4667
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4795
4668
  } else if (anchorIndex >= 0) {
4796
4669
  isReady = false;
4797
4670
  }
4798
4671
  }
4799
- const didSizeChange = previousSize !== nextSize;
4672
+ const didSizeChange = previousSize !== nextSize && (previousSize !== void 0 || anchoredEndSpace !== void 0);
4673
+ const didEffectiveSizeChange = (previousSize || 0) !== nextSize;
4800
4674
  const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
4801
4675
  if (isReady && (didSizeChange || didReadyAnchorChange)) {
4802
4676
  state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
@@ -4805,8 +4679,9 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4805
4679
  set$(ctx, "anchoredEndSpaceSize", nextSize);
4806
4680
  (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4807
4681
  }
4808
- if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
4809
- updateScroll(ctx, state.scroll, true);
4682
+ if (didEffectiveSizeChange) {
4683
+ updateContentMetricsState(ctx);
4684
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4810
4685
  }
4811
4686
  (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
4812
4687
  }
@@ -4824,16 +4699,10 @@ function runOrScheduleMVCPRecalculate(ctx) {
4824
4699
  }
4825
4700
  } else {
4826
4701
  if (!state.mvcpAnchorLock) {
4827
- if (state.queuedMVCPRecalculate !== void 0) {
4828
- cancelAnimationFrame(state.queuedMVCPRecalculate);
4829
- state.queuedMVCPRecalculate = void 0;
4830
- }
4702
+ state.scheduledWork.cancel("mvcpRecalculate");
4831
4703
  calculateItemsInView(ctx, { doMVCP: true });
4832
- } else if (state.queuedMVCPRecalculate === void 0) {
4833
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
4834
- state.queuedMVCPRecalculate = void 0;
4835
- calculateItemsInView(ctx, { doMVCP: true });
4836
- });
4704
+ } else if (!state.scheduledWork.has("mvcpRecalculate")) {
4705
+ state.scheduledWork.frame(() => calculateItemsInView(ctx, { doMVCP: true }), "mvcpRecalculate");
4837
4706
  }
4838
4707
  }
4839
4708
  }
@@ -4847,42 +4716,28 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
4847
4716
  }
4848
4717
  }
4849
4718
  }
4719
+ var activeItemSizeBatches;
4720
+ function batchItemSizeUpdates(runUpdates) {
4721
+ const isOuterBatch = activeItemSizeBatches === void 0;
4722
+ activeItemSizeBatches != null ? activeItemSizeBatches : activeItemSizeBatches = /* @__PURE__ */ new Map();
4723
+ try {
4724
+ runUpdates();
4725
+ } finally {
4726
+ if (isOuterBatch) {
4727
+ const batches = activeItemSizeBatches;
4728
+ activeItemSizeBatches = void 0;
4729
+ for (const [ctx, measurements] of batches) {
4730
+ updateItemSizesBatch(ctx, measurements);
4731
+ }
4732
+ }
4733
+ }
4734
+ }
4850
4735
  function mergeItemSizeUpdateResult(result, next) {
4851
4736
  result.didChange || (result.didChange = next.didChange);
4852
4737
  result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4853
4738
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4854
4739
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4855
4740
  }
4856
- var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4857
- function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4858
- const result = batchedItemSizeRecalculates.get(ctx);
4859
- if (!result || expectedResult && result !== expectedResult) {
4860
- return;
4861
- }
4862
- batchedItemSizeRecalculates.delete(ctx);
4863
- if (didFallback) {
4864
- ctx.state.pendingLayoutEffectMeasurements = void 0;
4865
- }
4866
- flushItemSizeUpdates(ctx, result);
4867
- }
4868
- function queueItemSizeRecalculate(ctx, result) {
4869
- var _a3, _b;
4870
- const batch = batchedItemSizeRecalculates.get(ctx);
4871
- if (batch) {
4872
- mergeItemSizeUpdateResult(batch, result);
4873
- } else {
4874
- const nextBatch = { ...result };
4875
- batchedItemSizeRecalculates.set(ctx, nextBatch);
4876
- if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4877
- requestAnimationFrame(() => {
4878
- flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4879
- });
4880
- }
4881
- }
4882
- if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4883
- flushBatchedItemSizeRecalculate(ctx);
4884
- }
4885
- }
4886
4741
  function flushItemSizeUpdates(ctx, result) {
4887
4742
  var _a3;
4888
4743
  const state = ctx.state;
@@ -4892,59 +4747,44 @@ function flushItemSizeUpdates(ctx, result) {
4892
4747
  } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4893
4748
  state.userScrollAnchorReset = void 0;
4894
4749
  }
4750
+ if (result.didChange) {
4751
+ maybeUpdateAnchoredEndSpace(ctx);
4752
+ }
4895
4753
  if (result.didChange && result.shouldMaintainScrollAtEnd) {
4896
4754
  doMaintainScrollAtEnd(ctx);
4897
4755
  }
4898
4756
  }
4899
4757
  function updateItemSizes(ctx, measurement) {
4900
- var _a3, _b, _c, _d;
4901
- const state = ctx.state;
4902
- let didDrainLayoutEffectMeasurements = false;
4903
- if (measurement.fromLayoutEffect) {
4904
- const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4905
- if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4906
- state.pendingLayoutEffectMeasurements = void 0;
4907
- didDrainLayoutEffectMeasurements = true;
4908
- }
4909
- }
4910
- const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4911
- const shouldBatchPendingMeasurements = measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4912
- const shouldQueueRecalculate = !!measurement.fromLayoutEffect;
4913
- let result;
4914
- if (!shouldBatchPendingMeasurements) {
4915
- result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4916
- } else {
4917
- result = {};
4918
- const updateContainerItemSize = (itemKey, containerId, size) => {
4919
- if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4920
- mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
4921
- }
4922
- };
4923
- updateContainerItemSize(measurement.itemKey, measurement.containerId, measurement.size);
4924
- const keys = Array.from(pendingKeys);
4925
- for (const itemKey of keys) {
4926
- const containerId = state.containerItemKeys.get(itemKey);
4927
- if (containerId !== void 0) {
4928
- (_d = (_c = (_b = ctx.viewRefs.get(containerId)) == null ? void 0 : _b.current) == null ? void 0 : _c.measure) == null ? void 0 : _d.call(_c, (_x, _y, width, height) => {
4929
- if (pendingKeys.has(itemKey)) {
4930
- updateContainerItemSize(itemKey, containerId, { height, width });
4931
- }
4932
- });
4933
- }
4758
+ if (activeItemSizeBatches) {
4759
+ const measurements = activeItemSizeBatches.get(ctx);
4760
+ if (measurements) {
4761
+ measurements.push(measurement);
4762
+ } else {
4763
+ activeItemSizeBatches.set(ctx, [measurement]);
4934
4764
  }
4935
- }
4936
- if (shouldQueueRecalculate && result.needsRecalculate) {
4937
- queueItemSizeRecalculate(ctx, result);
4938
4765
  } else {
4939
- flushItemSizeUpdates(ctx, result);
4940
- }
4941
- if (didDrainLayoutEffectMeasurements) {
4942
- flushBatchedItemSizeRecalculate(ctx);
4766
+ updateItemSizesBatch(ctx, [measurement]);
4943
4767
  }
4944
4768
  }
4945
- function applyItemSize(ctx, itemKey, sizeObj) {
4769
+ function updateItemSizesBatch(ctx, measurements) {
4946
4770
  var _a3;
4947
4771
  const state = ctx.state;
4772
+ const result = {};
4773
+ for (const measurement of measurements) {
4774
+ const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4775
+ if (ownsMeasuredItem) {
4776
+ const index = state.indexByKey.get(measurement.itemKey);
4777
+ const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
4778
+ const metadata = measurement.containerId !== void 0 && index !== void 0 && itemData !== void 0 ? resolveContainerItemMetadata(state, measurement.containerId, index, itemData) : void 0;
4779
+ const nextResult = applyItemSize(ctx, measurement.itemKey, measurement.size, metadata);
4780
+ mergeItemSizeUpdateResult(result, nextResult);
4781
+ }
4782
+ }
4783
+ flushItemSizeUpdates(ctx, result);
4784
+ }
4785
+ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4786
+ var _a3, _b;
4787
+ const state = ctx.state;
4948
4788
  const userScrollAnchorReset = state.userScrollAnchorReset;
4949
4789
  const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
4950
4790
  const {
@@ -4954,7 +4794,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
4954
4794
  } = state;
4955
4795
  if (!data) return { didMeasureUserScrollAnchorResetItem };
4956
4796
  const index = state.indexByKey.get(itemKey);
4957
- let resolvedMeasurementItem;
4958
4797
  if (getFixedItemSize) {
4959
4798
  if (index === void 0) {
4960
4799
  return { didMeasureUserScrollAnchorResetItem };
@@ -4963,14 +4802,15 @@ function applyItemSize(ctx, itemKey, sizeObj) {
4963
4802
  if (itemData === void 0) {
4964
4803
  return { didMeasureUserScrollAnchorResetItem };
4965
4804
  }
4966
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4967
- const size2 = getFixedItemSize(itemData, index, type);
4968
- resolvedMeasurementItem = {
4969
- didResolveFixedItemSize: true,
4970
- fixedItemSize: size2,
4971
- itemData,
4972
- itemType: type
4973
- };
4805
+ if (!(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4806
+ const type = (_b = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType) != null ? _b : getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4807
+ resolvedMeasurementItem = {
4808
+ didResolveFixedItemSize: true,
4809
+ fixedItemSize: getFixedItemSize(itemData, index, type),
4810
+ itemType: type
4811
+ };
4812
+ }
4813
+ const size2 = resolvedMeasurementItem.fixedItemSize;
4974
4814
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
4975
4815
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4976
4816
  return { didMeasureUserScrollAnchorResetItem };
@@ -4999,7 +4839,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
4999
4839
  previous: size - diff,
5000
4840
  size
5001
4841
  });
5002
- maybeUpdateAnchoredEndSpace(ctx);
5003
4842
  }
5004
4843
  if (minIndexSizeChanged !== void 0) {
5005
4844
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5020,7 +4859,7 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5020
4859
  };
5021
4860
  }
5022
4861
  function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5023
- var _a3, _b, _c;
4862
+ var _a3, _b;
5024
4863
  const state = ctx.state;
5025
4864
  const {
5026
4865
  indexByKey,
@@ -5030,11 +4869,11 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5030
4869
  } = state;
5031
4870
  if (!data) return 0;
5032
4871
  const index = indexByKey.get(itemKey);
5033
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
4872
+ const itemData = data[index];
5034
4873
  let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5035
4874
  let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5036
4875
  if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5037
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
4876
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5038
4877
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
5039
4878
  }
5040
4879
  const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
@@ -5048,26 +4887,346 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5048
4887
  const size = Math.round(rawSize) ;
5049
4888
  sizesKnown.set(itemKey, size);
5050
4889
  if (fixedItemSize === void 0 && size > 0) {
5051
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
4890
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5052
4891
  let averages = averageSizes[itemType];
5053
4892
  if (!averages) {
5054
4893
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
5055
4894
  }
5056
- if (averages.num === 0) {
5057
- averages.avg = size;
5058
- averages.num++;
5059
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5060
- averages.avg += (size - prevSizeKnown) / averages.num;
5061
- } else {
5062
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5063
- averages.num++;
4895
+ if (averages.num === 0) {
4896
+ averages.avg = size;
4897
+ averages.num++;
4898
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
4899
+ averages.avg += (size - prevSizeKnown) / averages.num;
4900
+ } else {
4901
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
4902
+ averages.num++;
4903
+ }
4904
+ }
4905
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
4906
+ setSize(ctx, itemKey, size);
4907
+ return size - prevSize;
4908
+ }
4909
+ return 0;
4910
+ }
4911
+
4912
+ // src/core/measureContainersInLayoutEffect.ts
4913
+ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
4914
+ const measurements = [];
4915
+ const containerIds = targetContainerIds != null ? targetContainerIds : ctx.viewRefs.keys();
4916
+ for (const containerId of containerIds) {
4917
+ const viewRef = ctx.viewRefs.get(containerId);
4918
+ const itemKey = peek$(ctx, `containerItemKey${containerId}`);
4919
+ const element = viewRef == null ? void 0 : viewRef.current;
4920
+ if (itemKey !== void 0 && element) {
4921
+ const rect = element.getBoundingClientRect();
4922
+ setContainerLayoutBaseline(element, rect);
4923
+ measurements.push({
4924
+ containerId,
4925
+ itemKey,
4926
+ size: { height: rect.height, width: rect.width }
4927
+ });
4928
+ }
4929
+ }
4930
+ if (measurements.length > 0) {
4931
+ updateItemSizesBatch(ctx, measurements);
4932
+ }
4933
+ }
4934
+ var typedForwardRef = React3.forwardRef;
4935
+ var typedMemo = React3.memo;
4936
+
4937
+ // src/components/ContainerLayoutCoordinator.tsx
4938
+ var ContainerLayoutCoordinator = typedMemo(function ContainerLayoutCoordinatorComponent({
4939
+ children
4940
+ }) {
4941
+ const ctx = useStateContext();
4942
+ const [containerLayoutEpoch] = useArr$(["containerLayoutEpoch"]);
4943
+ React3.useLayoutEffect(() => {
4944
+ {
4945
+ const targetContainerIds = getContainerLayoutEffectScope(ctx);
4946
+ if (targetContainerIds !== void 0) {
4947
+ measureContainersInLayoutEffect(ctx, targetContainerIds);
4948
+ }
4949
+ }
4950
+ }, [ctx, containerLayoutEpoch]);
4951
+ return children;
4952
+ });
4953
+ var getComponent = (Component) => {
4954
+ if (React3.isValidElement(Component)) {
4955
+ return Component;
4956
+ }
4957
+ if (Component) {
4958
+ return /* @__PURE__ */ React3.createElement(Component, null);
4959
+ }
4960
+ return null;
4961
+ };
4962
+
4963
+ // src/components/PositionView.tsx
4964
+ var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
4965
+ var baseCss = {
4966
+ contain: "paint layout style",
4967
+ ...isRNWeb ? {
4968
+ display: "flex",
4969
+ flexDirection: "column"
4970
+ } : {}
4971
+ };
4972
+ var PositionViewState = typedMemo(function PositionViewState2({
4973
+ id,
4974
+ horizontal,
4975
+ style,
4976
+ refView,
4977
+ ...props
4978
+ }) {
4979
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
4980
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
4981
+ const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
4982
+ const {
4983
+ animatedScrollY: _animatedScrollY,
4984
+ onLayout: _onLayout,
4985
+ onLayoutChange: _onLayoutChange,
4986
+ stickyHeaderConfig: _stickyHeaderConfig,
4987
+ ...webProps
4988
+ } = props;
4989
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
4990
+ });
4991
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
4992
+ id,
4993
+ horizontal,
4994
+ style,
4995
+ refView,
4996
+ animatedScrollY: _animatedScrollY,
4997
+ stickyHeaderConfig,
4998
+ onLayout: _onLayout,
4999
+ onLayoutChange: _onLayoutChange,
5000
+ children,
5001
+ ...webProps
5002
+ }) {
5003
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex, itemIndex] = useArr$([
5004
+ `containerPosition${id}`,
5005
+ "activeStickyIndex",
5006
+ `containerItemIndex${id}`
5007
+ ]);
5008
+ const composed = React3.useMemo(
5009
+ () => {
5010
+ var _a3;
5011
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
5012
+ },
5013
+ [style]
5014
+ );
5015
+ const viewStyle = React3.useMemo(() => {
5016
+ var _a3;
5017
+ const styleBase = { ...baseCss, ...composed };
5018
+ delete styleBase.transform;
5019
+ const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
5020
+ const isActive = activeStickyIndex === itemIndex;
5021
+ styleBase.position = isActive ? "sticky" : "absolute";
5022
+ styleBase.zIndex = itemIndex + 1e3;
5023
+ if (horizontal) {
5024
+ styleBase.left = isActive ? offset : position;
5025
+ } else {
5026
+ styleBase.top = isActive ? offset : position;
5027
+ }
5028
+ return styleBase;
5029
+ }, [composed, horizontal, position, itemIndex, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
5030
+ const renderStickyHeaderBackdrop = React3.useMemo(
5031
+ () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3.createElement(
5032
+ "div",
5033
+ {
5034
+ style: {
5035
+ inset: 0,
5036
+ pointerEvents: "none",
5037
+ position: "absolute"
5038
+ }
5039
+ },
5040
+ getComponent(stickyHeaderConfig.backdropComponent)
5041
+ ) : null,
5042
+ [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
5043
+ );
5044
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...webProps }, renderStickyHeaderBackdrop, children);
5045
+ });
5046
+ var PositionView = PositionViewState;
5047
+ function useInit(cb) {
5048
+ useState(() => cb());
5049
+ }
5050
+
5051
+ // src/state/ContextContainer.ts
5052
+ var ContextContainer = createContext(null);
5053
+ var NO_CONTAINER_ID = -1;
5054
+ function useContextContainer() {
5055
+ return useContext(ContextContainer);
5056
+ }
5057
+ function useContainerItemSignals(containerContext) {
5058
+ var _a3;
5059
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5060
+ const [itemKey, itemIndex, item] = useArr$([
5061
+ `containerItemKey${containerId}`,
5062
+ `containerItemIndex${containerId}`,
5063
+ `containerItemData${containerId}`
5064
+ ]);
5065
+ return {
5066
+ hasItemInfo: !!containerContext && itemKey !== void 0 && itemIndex !== void 0,
5067
+ item,
5068
+ itemIndex,
5069
+ itemKey
5070
+ };
5071
+ }
5072
+ function useAdaptiveRender() {
5073
+ const [mode] = useArr$(["adaptiveRender"]);
5074
+ return mode;
5075
+ }
5076
+ function useAdaptiveRenderChange(callback) {
5077
+ const ctx = useStateContext();
5078
+ const callbackRef = useRef(callback);
5079
+ callbackRef.current = callback;
5080
+ useLayoutEffect(() => {
5081
+ let mode = peek$(ctx, "adaptiveRender");
5082
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
5083
+ if (mode !== nextMode) {
5084
+ mode = nextMode;
5085
+ callbackRef.current(nextMode);
5086
+ }
5087
+ });
5088
+ }, [ctx]);
5089
+ }
5090
+ function useViewability(callback, configId) {
5091
+ const ctx = useStateContext();
5092
+ const containerContext = useContextContainer();
5093
+ useInit(() => {
5094
+ if (!containerContext) {
5095
+ return;
5096
+ }
5097
+ const { containerId } = containerContext;
5098
+ const key = containerId + (configId != null ? configId : "");
5099
+ const value = ctx.mapViewabilityValues.get(key);
5100
+ if (value) {
5101
+ callback(value);
5102
+ }
5103
+ });
5104
+ useEffect(() => {
5105
+ if (!containerContext) {
5106
+ return;
5107
+ }
5108
+ const { containerId } = containerContext;
5109
+ const key = containerId + (configId != null ? configId : "");
5110
+ ctx.mapViewabilityCallbacks.set(key, callback);
5111
+ return () => {
5112
+ ctx.mapViewabilityCallbacks.delete(key);
5113
+ };
5114
+ }, [ctx, callback, configId, containerContext]);
5115
+ }
5116
+ function useViewabilityAmount(callback) {
5117
+ const ctx = useStateContext();
5118
+ const containerContext = useContextContainer();
5119
+ useInit(() => {
5120
+ if (!containerContext) {
5121
+ return;
5122
+ }
5123
+ const { containerId } = containerContext;
5124
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
5125
+ if (value) {
5126
+ callback(value);
5127
+ }
5128
+ });
5129
+ useEffect(() => {
5130
+ if (!containerContext) {
5131
+ return;
5132
+ }
5133
+ const { containerId } = containerContext;
5134
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
5135
+ return () => {
5136
+ ctx.mapViewabilityAmountCallbacks.delete(containerId);
5137
+ };
5138
+ }, [ctx, callback, containerContext]);
5139
+ }
5140
+ function useRecyclingEffect(effect) {
5141
+ const containerContext = useContextContainer();
5142
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5143
+ const prevInfo = useRef(void 0);
5144
+ useEffect(() => {
5145
+ if (!hasItemInfo) {
5146
+ return;
5147
+ }
5148
+ let ret;
5149
+ if (prevInfo.current) {
5150
+ ret = effect({
5151
+ index: itemIndex,
5152
+ item,
5153
+ prevIndex: prevInfo.current.index,
5154
+ prevItem: prevInfo.current.item
5155
+ });
5156
+ }
5157
+ prevInfo.current = {
5158
+ index: itemIndex,
5159
+ item
5160
+ };
5161
+ return ret;
5162
+ }, [effect, hasItemInfo, itemIndex, item, itemKey]);
5163
+ }
5164
+ function useRecyclingState(valueOrFun) {
5165
+ const containerContext = useContextContainer();
5166
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5167
+ const computeValue = () => {
5168
+ if (isFunction(valueOrFun)) {
5169
+ const initializer = valueOrFun;
5170
+ return hasItemInfo ? initializer({
5171
+ index: itemIndex,
5172
+ item,
5173
+ prevIndex: void 0,
5174
+ prevItem: void 0
5175
+ }) : initializer();
5064
5176
  }
5177
+ return valueOrFun;
5178
+ };
5179
+ const [stateValue, setStateValue] = useState(() => {
5180
+ return computeValue();
5181
+ });
5182
+ const prevItemKeyRef = useRef(hasItemInfo ? itemKey : null);
5183
+ if (hasItemInfo && prevItemKeyRef.current !== itemKey) {
5184
+ prevItemKeyRef.current = itemKey;
5185
+ setStateValue(computeValue());
5065
5186
  }
5066
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5067
- setSize(ctx, itemKey, size);
5068
- return size - prevSize;
5069
- }
5070
- return 0;
5187
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
5188
+ const setState = useCallback(
5189
+ (newState) => {
5190
+ if (!triggerLayout) {
5191
+ return;
5192
+ }
5193
+ setStateValue((prevValue) => {
5194
+ return isFunction(newState) ? newState(prevValue) : newState;
5195
+ });
5196
+ triggerLayout();
5197
+ },
5198
+ [triggerLayout]
5199
+ );
5200
+ return [stateValue, setState];
5201
+ }
5202
+ function useIsLastItem() {
5203
+ var _a3;
5204
+ const containerContext = useContextContainer();
5205
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5206
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
5207
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
5208
+ if (containerContext && !isNullOrUndefined(itemKey)) {
5209
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
5210
+ }
5211
+ return false;
5212
+ });
5213
+ return isLast;
5214
+ }
5215
+ function useListScrollSize() {
5216
+ const [scrollSize] = useArr$(["scrollSize"]);
5217
+ return scrollSize;
5218
+ }
5219
+ var noop = () => {
5220
+ };
5221
+ function useSyncLayout() {
5222
+ const containerContext = useContextContainer();
5223
+ return containerContext ? containerContext.triggerLayout : noop;
5224
+ }
5225
+
5226
+ // src/components/Separator.tsx
5227
+ function Separator({ ItemSeparatorComponent, leadingItem }) {
5228
+ const isLastItem = useIsLastItem();
5229
+ return isLastItem ? null : /* @__PURE__ */ React3.createElement(ItemSeparatorComponent, { leadingItem });
5071
5230
  }
5072
5231
 
5073
5232
  // src/hooks/createResizeObserver.ts
@@ -5075,43 +5234,39 @@ var globalResizeObserver = null;
5075
5234
  function getGlobalResizeObserver() {
5076
5235
  if (!globalResizeObserver) {
5077
5236
  globalResizeObserver = new ResizeObserver((entries) => {
5078
- for (const entry of entries) {
5079
- const callbacks = callbackMap.get(entry.target);
5080
- if (callbacks) {
5081
- for (const callback of callbacks) {
5082
- callback(entry);
5237
+ batchItemSizeUpdates(() => {
5238
+ for (const entry of entries) {
5239
+ const callbacks = callbackMap.get(entry.target);
5240
+ if (callbacks) {
5241
+ for (const callback of callbacks) {
5242
+ callback(entry);
5243
+ }
5083
5244
  }
5084
5245
  }
5085
- }
5246
+ });
5086
5247
  });
5087
5248
  }
5088
5249
  return globalResizeObserver;
5089
5250
  }
5090
5251
  var callbackMap = /* @__PURE__ */ new WeakMap();
5091
5252
  function createResizeObserver(element, callback) {
5092
- if (typeof ResizeObserver === "undefined") {
5093
- return () => {
5094
- };
5095
- }
5096
- if (!element) {
5253
+ var _a3;
5254
+ if (typeof ResizeObserver === "undefined" || !element) {
5097
5255
  return () => {
5098
5256
  };
5099
5257
  }
5100
5258
  const observer = getGlobalResizeObserver();
5101
- let callbacks = callbackMap.get(element);
5102
- if (!callbacks) {
5103
- callbacks = /* @__PURE__ */ new Set();
5259
+ const callbacks = (_a3 = callbackMap.get(element)) != null ? _a3 : /* @__PURE__ */ new Set();
5260
+ if (callbacks.size === 0) {
5104
5261
  callbackMap.set(element, callbacks);
5105
- observer.observe(element);
5262
+ observer.observe(element, { box: "border-box" });
5106
5263
  }
5107
5264
  callbacks.add(callback);
5108
5265
  return () => {
5109
- if (callbacks) {
5110
- callbacks.delete(callback);
5111
- if (callbacks.size === 0) {
5112
- callbackMap.delete(element);
5113
- observer.unobserve(element);
5114
- }
5266
+ callbacks.delete(callback);
5267
+ if (callbacks.size === 0) {
5268
+ callbackMap.delete(element);
5269
+ observer.unobserve(element);
5115
5270
  }
5116
5271
  };
5117
5272
  }
@@ -5119,6 +5274,7 @@ function createResizeObserver(element, callback) {
5119
5274
  // src/hooks/useOnLayoutSync.tsx
5120
5275
  function useOnLayoutSync({
5121
5276
  ref,
5277
+ measureInLayoutEffect = true,
5122
5278
  onLayoutProp,
5123
5279
  onLayoutChange,
5124
5280
  webLayoutResync
@@ -5138,21 +5294,31 @@ function useOnLayoutSync({
5138
5294
  onLayoutChange(layout, fromLayoutEffect);
5139
5295
  onLayoutProp == null ? void 0 : onLayoutProp({ nativeEvent: { layout } });
5140
5296
  };
5141
- const rect = element.getBoundingClientRect();
5142
- emit(toLayout(rect), true);
5143
- let prevRect = rect;
5297
+ let prevRect;
5298
+ if (measureInLayoutEffect) {
5299
+ const rect = element.getBoundingClientRect();
5300
+ emit(toLayout(rect), true);
5301
+ prevRect = rect;
5302
+ }
5144
5303
  return createResizeObserver(element, (entry) => {
5145
5304
  var _a4;
5146
5305
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
5147
- const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
5148
- const didSizeChange = rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height;
5306
+ const borderBoxSize = Array.isArray(entry.borderBoxSize) ? entry.borderBoxSize[0] : entry.borderBoxSize;
5307
+ const rectObserved = borderBoxSize ? {
5308
+ height: borderBoxSize.blockSize,
5309
+ left: entry.contentRect.left,
5310
+ top: entry.contentRect.top,
5311
+ width: borderBoxSize.inlineSize
5312
+ } : (_a4 = target == null ? void 0 : target.getBoundingClientRect()) != null ? _a4 : entry.contentRect;
5313
+ const previousRect = prevRect != null ? prevRect : getContainerLayoutBaseline(element);
5314
+ const didSizeChange = previousRect === void 0 || rectObserved.width !== previousRect.width || rectObserved.height !== previousRect.height;
5315
+ prevRect = rectObserved;
5149
5316
  const shouldResyncLayout = !!(webLayoutResync == null ? void 0 : webLayoutResync());
5150
5317
  if (didSizeChange || shouldResyncLayout) {
5151
- prevRect = rectObserved;
5152
5318
  emit(toLayout(rectObserved), false);
5153
5319
  }
5154
5320
  });
5155
- }, deps || []);
5321
+ }, [measureInLayoutEffect, ...deps || []]);
5156
5322
  return {};
5157
5323
  }
5158
5324
  function toLayout(rect) {
@@ -5167,6 +5333,121 @@ function toLayout(rect) {
5167
5333
  };
5168
5334
  }
5169
5335
 
5336
+ // src/hooks/useContainerMeasurement.tsx
5337
+ var pendingWebShrinkMeasurements = /* @__PURE__ */ new Map();
5338
+ var pendingWebShrinkFrame;
5339
+ function cancelWebShrinkMeasurement(state) {
5340
+ pendingWebShrinkMeasurements.delete(state);
5341
+ }
5342
+ function scheduleWebShrinkMeasurement(state, confirmMeasurement) {
5343
+ pendingWebShrinkMeasurements.set(state, confirmMeasurement);
5344
+ if (pendingWebShrinkFrame === void 0) {
5345
+ pendingWebShrinkFrame = requestAnimationFrame(() => {
5346
+ const callbacks = Array.from(pendingWebShrinkMeasurements.values());
5347
+ pendingWebShrinkMeasurements.clear();
5348
+ pendingWebShrinkFrame = void 0;
5349
+ batchItemSizeUpdates(() => {
5350
+ for (const callback of callbacks) {
5351
+ callback();
5352
+ }
5353
+ });
5354
+ });
5355
+ }
5356
+ }
5357
+ function processContainerLayout({ containerId, ctx, rectangle, ref, state }) {
5358
+ const listState = ctx.state;
5359
+ const currentItemKey = state.itemKey;
5360
+ state.didLayout = true;
5361
+ let layout = rectangle;
5362
+ const axis = state.horizontal ? "width" : "height";
5363
+ const size = roundSize(rectangle[axis]);
5364
+ state.lastSize ? roundSize(state.lastSize[axis]) : void 0;
5365
+ const coreKnownSize = listState.sizesKnown.get(currentItemKey);
5366
+ const previousSize = coreKnownSize ;
5367
+ const applyLayout = () => {
5368
+ state.lastSize = layout;
5369
+ updateItemSizes(ctx, {
5370
+ containerId,
5371
+ itemKey: currentItemKey,
5372
+ size: layout
5373
+ });
5374
+ };
5375
+ const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(listState) && previousSize !== void 0 && size + 1 < previousSize;
5376
+ if (shouldDeferWebShrinkLayoutUpdate) {
5377
+ scheduleWebShrinkMeasurement(state, () => {
5378
+ var _a4;
5379
+ if (state.itemKey === currentItemKey) {
5380
+ const element = ref.current;
5381
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5382
+ if (rect) {
5383
+ layout = { height: rect.height, width: rect.width };
5384
+ }
5385
+ applyLayout();
5386
+ }
5387
+ });
5388
+ } else {
5389
+ {
5390
+ cancelWebShrinkMeasurement(state);
5391
+ }
5392
+ {
5393
+ applyLayout();
5394
+ }
5395
+ }
5396
+ }
5397
+ function useContainerMeasurement({
5398
+ containerId,
5399
+ ctx,
5400
+ horizontal,
5401
+ itemKey,
5402
+ ref
5403
+ }) {
5404
+ const stateRef = useRef({
5405
+ didLayout: false,
5406
+ horizontal,
5407
+ itemKey
5408
+ });
5409
+ stateRef.current.horizontal = horizontal;
5410
+ stateRef.current.itemKey = itemKey;
5411
+ const [layoutRenderCount, forceLayoutRender] = useState(0);
5412
+ const onLayoutChange = useCallback(
5413
+ (rectangle) => {
5414
+ processContainerLayout({ containerId, ctx, rectangle, ref, state: stateRef.current });
5415
+ },
5416
+ [containerId, ctx, ref]
5417
+ );
5418
+ const triggerLayout = useCallback(() => {
5419
+ {
5420
+ scheduleContainerLayout(ctx, containerId);
5421
+ }
5422
+ }, [containerId, ctx]);
5423
+ useLayoutEffect(() => {
5424
+ ctx.containerLayoutTriggers.set(containerId, triggerLayout);
5425
+ return () => {
5426
+ cancelWebShrinkMeasurement(stateRef.current);
5427
+ if (ctx.containerLayoutTriggers.get(containerId) === triggerLayout) {
5428
+ ctx.containerLayoutTriggers.delete(containerId);
5429
+ }
5430
+ };
5431
+ }, [containerId, ctx, triggerLayout]);
5432
+ useLayoutEffect(() => {
5433
+ {
5434
+ scheduleContainerLayout(ctx, containerId);
5435
+ }
5436
+ });
5437
+ const { onLayout } = useOnLayoutSync(
5438
+ {
5439
+ measureInLayoutEffect: !IsNewArchitecture,
5440
+ onLayoutChange,
5441
+ ref,
5442
+ webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5443
+ },
5444
+ [itemKey, layoutRenderCount]
5445
+ );
5446
+ useEffect(() => {
5447
+ }, [containerId, ctx, itemKey]);
5448
+ return { onLayout, triggerLayout };
5449
+ }
5450
+
5170
5451
  // src/components/Container.tsx
5171
5452
  function getContainerPositionStyle({
5172
5453
  columnWrapperStyle,
@@ -5237,16 +5518,14 @@ var Container = typedMemo(function Container2({
5237
5518
  "extraData",
5238
5519
  `containerSticky${id}`
5239
5520
  ]);
5240
- const itemLayoutRef = useRef({
5241
- didLayout: false,
5521
+ const ref = useRef(null);
5522
+ const { onLayout, triggerLayout } = useContainerMeasurement({
5523
+ containerId: id,
5524
+ ctx,
5242
5525
  horizontal,
5243
5526
  itemKey,
5244
- pendingShrinkToken: 0
5527
+ ref
5245
5528
  });
5246
- itemLayoutRef.current.horizontal = horizontal;
5247
- itemLayoutRef.current.itemKey = itemKey;
5248
- const ref = useRef(null);
5249
- const [layoutRenderCount, forceLayoutRender] = useState(0);
5250
5529
  const resolvedColumn = column > 0 ? column : 1;
5251
5530
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
5252
5531
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
@@ -5277,57 +5556,7 @@ var Container = typedMemo(function Container2({
5277
5556
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
5278
5557
  [itemKey, data, extraData]
5279
5558
  );
5280
- const { index, renderedItem } = renderedItemInfo || {};
5281
- const onLayoutChange = useCallback((rectangle, fromLayoutEffect) => {
5282
- const {
5283
- horizontal: currentHorizontal,
5284
- itemKey: currentItemKey,
5285
- lastSize,
5286
- pendingShrinkToken
5287
- } = itemLayoutRef.current;
5288
- if (isNullOrUndefined(currentItemKey)) {
5289
- return;
5290
- }
5291
- itemLayoutRef.current.didLayout = true;
5292
- let layout = rectangle;
5293
- const axis = currentHorizontal ? "width" : "height";
5294
- const size = roundSize(rectangle[axis]);
5295
- const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
5296
- const doUpdate = () => {
5297
- itemLayoutRef.current.lastSize = layout;
5298
- updateItemSizes(ctx, {
5299
- containerId: id,
5300
- fromLayoutEffect,
5301
- itemKey: currentItemKey,
5302
- size: layout
5303
- });
5304
- itemLayoutRef.current.didLayout = true;
5305
- };
5306
- const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
5307
- if (shouldDeferWebShrinkLayoutUpdate) {
5308
- const token = pendingShrinkToken + 1;
5309
- itemLayoutRef.current.pendingShrinkToken = token;
5310
- requestAnimationFrame(() => {
5311
- var _a4;
5312
- if (itemLayoutRef.current.pendingShrinkToken !== token) {
5313
- return;
5314
- }
5315
- const element = ref.current;
5316
- const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5317
- if (rect) {
5318
- layout = { height: rect.height, width: rect.width };
5319
- }
5320
- doUpdate();
5321
- });
5322
- return;
5323
- }
5324
- {
5325
- doUpdate();
5326
- }
5327
- }, []);
5328
- const triggerLayout = useCallback(() => {
5329
- forceLayoutRender((v) => v + 1);
5330
- }, []);
5559
+ const { renderedItem } = renderedItemInfo || {};
5331
5560
  const contextValue = useMemo(() => {
5332
5561
  ctx.viewRefs.set(id, ref);
5333
5562
  return {
@@ -5335,22 +5564,6 @@ var Container = typedMemo(function Container2({
5335
5564
  triggerLayout
5336
5565
  };
5337
5566
  }, [id, triggerLayout]);
5338
- useLayoutEffect(() => {
5339
- ctx.containerLayoutTriggers.set(id, triggerLayout);
5340
- return () => {
5341
- if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
5342
- ctx.containerLayoutTriggers.delete(id);
5343
- }
5344
- };
5345
- }, [ctx, id, triggerLayout]);
5346
- const { onLayout } = useOnLayoutSync(
5347
- {
5348
- onLayoutChange,
5349
- ref,
5350
- webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5351
- },
5352
- [itemKey, layoutRenderCount]
5353
- );
5354
5567
  const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
5355
5568
  return /* @__PURE__ */ React3.createElement(
5356
5569
  PositionComponent,
@@ -5358,7 +5571,6 @@ var Container = typedMemo(function Container2({
5358
5571
  animatedScrollY: isSticky ? animatedScrollY : void 0,
5359
5572
  horizontal,
5360
5573
  id,
5361
- index,
5362
5574
  key: recycleItems ? void 0 : itemKey,
5363
5575
  onLayout,
5364
5576
  refView: ref,
@@ -5401,18 +5613,13 @@ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
5401
5613
  });
5402
5614
 
5403
5615
  // src/utils/reordering.ts
5404
- var mapFn = (element) => {
5405
- const indexStr = element.getAttribute("data-index");
5406
- if (indexStr === null) {
5407
- return [element, null];
5408
- }
5409
- const index = Number.parseInt(indexStr, 10);
5410
- return [element, Number.isNaN(index) ? null : index];
5411
- };
5412
- function sortDOMElements(container) {
5616
+ function sortDOMElements(container, indexByElement) {
5413
5617
  const elements = Array.from(container.children);
5414
5618
  if (elements.length <= 1) return elements;
5415
- const items = elements.map(mapFn);
5619
+ const items = elements.map((element) => {
5620
+ var _a3;
5621
+ return [element, (_a3 = indexByElement.get(element)) != null ? _a3 : null];
5622
+ });
5416
5623
  items.sort((a, b) => {
5417
5624
  const aKey = a[1];
5418
5625
  const bKey = b[1];
@@ -5500,7 +5707,15 @@ function useDOMOrder(ref) {
5500
5707
  debounceRef.current = setTimeout(() => {
5501
5708
  const parent = ref.current;
5502
5709
  if (parent) {
5503
- sortDOMElements(parent);
5710
+ const indexByElement = /* @__PURE__ */ new Map();
5711
+ for (const [containerId, viewRef] of ctx.viewRefs) {
5712
+ const element = viewRef.current;
5713
+ const index = peek$(ctx, `containerItemIndex${containerId}`);
5714
+ if (element && index !== void 0) {
5715
+ indexByElement.set(element, index);
5716
+ }
5717
+ }
5718
+ sortDOMElements(parent, indexByElement);
5504
5719
  }
5505
5720
  debounceRef.current = void 0;
5506
5721
  }, 500);
@@ -5513,23 +5728,40 @@ function useDOMOrder(ref) {
5513
5728
  };
5514
5729
  }, [ctx]);
5515
5730
  }
5731
+ function useFreshDataTransitionVisibility(readyToRender, transitionEpoch) {
5732
+ const completedTransitionEpoch = useRef(transitionEpoch);
5733
+ const isTransitionPending = completedTransitionEpoch.current !== transitionEpoch;
5734
+ useLayoutEffect(() => {
5735
+ if (!readyToRender) {
5736
+ completedTransitionEpoch.current = transitionEpoch;
5737
+ }
5738
+ }, [readyToRender, transitionEpoch]);
5739
+ return readyToRender && !isTransitionPending;
5740
+ }
5516
5741
 
5517
5742
  // src/components/Containers.tsx
5518
- var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColumns, children }) {
5743
+ var ContainersInner = typedMemo(function ContainersInner2({
5744
+ freshDataTransitionEpoch,
5745
+ horizontal,
5746
+ numColumns,
5747
+ children
5748
+ }) {
5519
5749
  const ref = useRef(null);
5520
5750
  const ctx = useStateContext();
5521
5751
  const columnWrapperStyle = ctx.columnWrapperStyle;
5522
5752
  const isHorizontalRTLList = isHorizontalRTL(ctx.state);
5523
5753
  const [otherAxisSize, readyToRender, totalSize] = useArr$(["otherAxisSize", "readyToRender", "totalSize"]);
5754
+ const isVisible = useFreshDataTransitionVisibility(!!readyToRender, freshDataTransitionEpoch);
5524
5755
  useDOMOrder(ref);
5525
5756
  const style = horizontal ? {
5526
5757
  direction: isHorizontalRTLList ? "ltr" : void 0,
5758
+ flexShrink: 0,
5527
5759
  minHeight: otherAxisSize,
5528
- opacity: readyToRender ? 1 : 0,
5760
+ opacity: isVisible ? 1 : 0,
5529
5761
  position: "relative",
5530
5762
  width: totalSize
5531
- } : { height: totalSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0, position: "relative" };
5532
- if (!readyToRender) {
5763
+ } : { height: totalSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0, position: "relative" };
5764
+ if (!isVisible) {
5533
5765
  style.pointerEvents = "none";
5534
5766
  }
5535
5767
  if (columnWrapperStyle && numColumns > 1) {
@@ -5549,9 +5781,10 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
5549
5781
  }
5550
5782
  }
5551
5783
  }
5552
- return /* @__PURE__ */ React3.createElement("div", { ref, style }, children);
5784
+ return /* @__PURE__ */ React3.createElement("div", { ref, style }, /* @__PURE__ */ React3.createElement(ContainerLayoutCoordinator, null, children));
5553
5785
  });
5554
5786
  var Containers = typedMemo(function Containers2({
5787
+ freshDataTransitionEpoch,
5555
5788
  horizontal,
5556
5789
  recycleItems,
5557
5790
  ItemSeparatorComponent,
@@ -5576,7 +5809,15 @@ var Containers = typedMemo(function Containers2({
5576
5809
  )
5577
5810
  );
5578
5811
  }
5579
- return /* @__PURE__ */ React3.createElement(ContainersInner, { horizontal, numColumns }, containers);
5812
+ return /* @__PURE__ */ React3.createElement(
5813
+ ContainersInner,
5814
+ {
5815
+ freshDataTransitionEpoch,
5816
+ horizontal,
5817
+ numColumns
5818
+ },
5819
+ containers
5820
+ );
5580
5821
  });
5581
5822
 
5582
5823
  // src/platform/StyleSheet.tsx
@@ -5728,6 +5969,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
5728
5969
 
5729
5970
  // src/components/ListComponentScrollView.tsx
5730
5971
  var SCROLLBAR_HIDDEN_STYLE_ID = "legend-list-scrollbar-axis-hidden-style";
5972
+ var SCROLL_END_FALLBACK_MS = 200;
5731
5973
  var SCROLLBAR_HIDDEN_STYLE = `.${LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS}::-webkit-scrollbar:vertical{width:0;display:none;}.${LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS}::-webkit-scrollbar:horizontal{height:0;display:none;}`;
5732
5974
  function ensureScrollbarHiddenStyle() {
5733
5975
  if (typeof document === "undefined" || document.getElementById(SCROLLBAR_HIDDEN_STYLE_ID)) {
@@ -5777,6 +6019,7 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5777
6019
  contentOffset,
5778
6020
  maintainVisibleContentPosition,
5779
6021
  onScroll: onScroll2,
6022
+ onInternalScrollEnd,
5780
6023
  onMomentumScrollEnd: _onMomentumScrollEnd,
5781
6024
  showsHorizontalScrollIndicator = true,
5782
6025
  showsVerticalScrollIndicator = true,
@@ -5785,7 +6028,7 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5785
6028
  onLayout,
5786
6029
  ...props
5787
6030
  }, ref) {
5788
- var _a3, _b, _c, _d;
6031
+ var _a3, _b, _c;
5789
6032
  const ctx = useStateContext();
5790
6033
  const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
5791
6034
  const scrollRef = useRef(null);
@@ -5906,6 +6149,16 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5906
6149
  onScroll2(scrollEvent);
5907
6150
  }, [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]);
5908
6151
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
6152
+ const scrollEndFallbackRef = useRef(void 0);
6153
+ const emitScrollEnd = useCallback(() => {
6154
+ const timeout = scrollEndFallbackRef.current;
6155
+ if (timeout !== void 0) {
6156
+ clearTimeout(timeout);
6157
+ scrollEndFallbackRef.current = void 0;
6158
+ }
6159
+ scrollEventCoalescer.flush();
6160
+ onInternalScrollEnd == null ? void 0 : onInternalScrollEnd();
6161
+ }, [onInternalScrollEnd, scrollEventCoalescer]);
5909
6162
  const handleScroll = useCallback(
5910
6163
  (_event) => {
5911
6164
  if (!onScroll2) {
@@ -5918,18 +6171,36 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5918
6171
  } else {
5919
6172
  scrollEventCoalescer.schedule();
5920
6173
  }
6174
+ if (onInternalScrollEnd) {
6175
+ const timeout = scrollEndFallbackRef.current;
6176
+ if (timeout !== void 0) {
6177
+ clearTimeout(timeout);
6178
+ }
6179
+ scrollEndFallbackRef.current = setTimeout(emitScrollEnd, SCROLL_END_FALLBACK_MS);
6180
+ }
5921
6181
  },
5922
- [ctx.state, onScroll2, scrollEventCoalescer]
6182
+ [ctx.state, emitScrollEnd, onInternalScrollEnd, onScroll2, scrollEventCoalescer]
5923
6183
  );
5924
6184
  useLayoutEffect(() => {
5925
6185
  const target = getScrollTarget();
5926
6186
  if (!target) return;
5927
6187
  target.addEventListener("scroll", handleScroll, { passive: true });
6188
+ if ("onscrollend" in target) {
6189
+ target.addEventListener("scrollend", emitScrollEnd);
6190
+ }
5928
6191
  return () => {
5929
6192
  target.removeEventListener("scroll", handleScroll);
6193
+ if ("onscrollend" in target) {
6194
+ target.removeEventListener("scrollend", emitScrollEnd);
6195
+ }
6196
+ const timeout = scrollEndFallbackRef.current;
6197
+ if (timeout !== void 0) {
6198
+ clearTimeout(timeout);
6199
+ scrollEndFallbackRef.current = void 0;
6200
+ }
5930
6201
  scrollEventCoalescer.cancel();
5931
6202
  };
5932
- }, [getScrollTarget, handleScroll, scrollEventCoalescer]);
6203
+ }, [emitScrollEnd, getScrollTarget, handleScroll, scrollEventCoalescer]);
5933
6204
  useEffect(() => {
5934
6205
  const doScroll = () => {
5935
6206
  if (contentOffset) {
@@ -5989,7 +6260,7 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5989
6260
  } : {}
5990
6261
  };
5991
6262
  const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd2(ctx);
5992
- 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;
6263
+ const anchoredEndInset = ((_b = (_a3 = ctx.state) == null ? void 0 : _a3.props) == null ? void 0 : _b.anchoredEndSpace) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
5993
6264
  const renderedContentInsetEndAdjustment = Math.max(0, contentInsetEndAdjustment - anchoredEndInset);
5994
6265
  const contentInsetEndAdjustmentSpacerStyle = renderedContentInsetEndAdjustment ? horizontal ? { flexShrink: 0, width: renderedContentInsetEndAdjustment } : { height: renderedContentInsetEndAdjustment } : void 0;
5995
6266
  const contentStyle = {
@@ -6016,7 +6287,7 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
6016
6287
  const snapOffsets = !isWindowScroll ? getFiniteSnapOffsets(snapToOffsets) : [];
6017
6288
  if (snapOffsets.length > 0) {
6018
6289
  scrollViewStyle.scrollSnapType = horizontal ? "x mandatory" : "y mandatory";
6019
- contentStyle.position = (_d = contentStyle.position) != null ? _d : "relative";
6290
+ contentStyle.position = (_c = contentStyle.position) != null ? _c : "relative";
6020
6291
  }
6021
6292
  const scrollViewClassName = hiddenScrollIndicatorClassName ? scrollViewClassNameProp ? `${scrollViewClassNameProp} ${hiddenScrollIndicatorClassName}` : hiddenScrollIndicatorClassName : scrollViewClassNameProp;
6022
6293
  if (IS_DEV) {
@@ -6165,16 +6436,6 @@ var SnapWrapper = React3.forwardRef(function SnapWrapperInner({ ScrollComponent,
6165
6436
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
6166
6437
  return /* @__PURE__ */ React3.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
6167
6438
  });
6168
- function WebAnchoredEndSpace({ horizontal }) {
6169
- const ctx = useStateContext();
6170
- const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
6171
- const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
6172
- if (!shouldRenderAnchoredEndSpace) {
6173
- return null;
6174
- }
6175
- const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
6176
- return /* @__PURE__ */ React3.createElement("div", { style }, null);
6177
- }
6178
6439
 
6179
6440
  // src/core/updateContentMetrics.ts
6180
6441
  var SCROLL_ADJUST_EPSILON = 0.1;
@@ -6208,7 +6469,11 @@ function setHeaderSize(ctx, size) {
6208
6469
  state.didMeasureHeader = true;
6209
6470
  }
6210
6471
  function setFooterSize(ctx, size) {
6211
- return setContentLengthSignal(ctx, "footerSize", size);
6472
+ const didChange = setContentLengthSignal(ctx, "footerSize", size);
6473
+ if (didChange) {
6474
+ maybeUpdateAnchoredEndSpace(ctx);
6475
+ }
6476
+ return didChange;
6212
6477
  }
6213
6478
  function areInsetsEqual(left, right) {
6214
6479
  var _a3, _b, _c, _d, _e, _f, _g, _h;
@@ -6261,6 +6526,7 @@ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizont
6261
6526
  });
6262
6527
  var ListComponent = typedMemo(function ListComponent2({
6263
6528
  canRender,
6529
+ freshDataTransitionEpoch,
6264
6530
  style,
6265
6531
  contentContainerStyle,
6266
6532
  horizontal,
@@ -6279,6 +6545,8 @@ var ListComponent = typedMemo(function ListComponent2({
6279
6545
  refScrollView,
6280
6546
  renderScrollComponent,
6281
6547
  onLayoutFooter,
6548
+ onInternalScrollBeginDrag,
6549
+ onInternalScrollEnd,
6282
6550
  scrollAdjustHandler,
6283
6551
  snapToIndices,
6284
6552
  stickyHeaderConfig,
@@ -6288,8 +6556,17 @@ var ListComponent = typedMemo(function ListComponent2({
6288
6556
  }) {
6289
6557
  const ctx = useStateContext();
6290
6558
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
6291
- const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
6559
+ const [anchoredEndSpaceSize = 0, otherAxisSize = 0] = useArr$(["anchoredEndSpaceSize", "otherAxisSize"]);
6292
6560
  const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
6561
+ const shouldMaterializeAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && ctx.state.props.anchoredEndSpaceOwner === "list" && anchoredEndSpaceSize > 0;
6562
+ let anchoredEndSpaceStyle;
6563
+ if (shouldMaterializeAnchoredEndSpace) {
6564
+ const paddingEnd = horizontal ? isHorizontalRTL(ctx.state) ? "Left" : "Right" : "Bottom";
6565
+ const flattenedContentContainerStyle = StyleSheet.flatten(contentContainerStyle);
6566
+ anchoredEndSpaceStyle = {
6567
+ [`padding${paddingEnd}`]: extractPadding({}, flattenedContentContainerStyle || {}, paddingEnd) + anchoredEndSpaceSize
6568
+ };
6569
+ }
6293
6570
  const autoOtherAxisStyle = getAutoOtherAxisStyle({
6294
6571
  horizontal,
6295
6572
  needsOtherAxisSize: ctx.state.needsOtherAxisSize,
@@ -6340,12 +6617,12 @@ var ListComponent = typedMemo(function ListComponent2({
6340
6617
  SnapOrScroll,
6341
6618
  {
6342
6619
  ...rest,
6343
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
6620
+ ...ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} ,
6344
6621
  contentContainerStyle: [
6345
- horizontal ? {
6346
- height: "100%"
6347
- } : {},
6348
- contentContainerStyle
6622
+ horizontal ? { height: "100%" } : {},
6623
+ contentContainerStyle,
6624
+ anchoredEndSpaceStyle,
6625
+ { boxSizing: "border-box" }
6349
6626
  ],
6350
6627
  contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
6351
6628
  horizontal,
@@ -6363,6 +6640,7 @@ var ListComponent = typedMemo(function ListComponent2({
6363
6640
  canRender && !ListEmptyComponent && /* @__PURE__ */ React3.createElement(
6364
6641
  Containers,
6365
6642
  {
6643
+ freshDataTransitionEpoch,
6366
6644
  getRenderedItem: getRenderedItem2,
6367
6645
  horizontal,
6368
6646
  ItemSeparatorComponent,
@@ -6371,7 +6649,6 @@ var ListComponent = typedMemo(function ListComponent2({
6371
6649
  }
6372
6650
  ),
6373
6651
  ListFooterComponent && /* @__PURE__ */ React3.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
6374
- /* @__PURE__ */ React3.createElement(WebAnchoredEndSpace, { horizontal }),
6375
6652
  IS_DEV && ENABLE_DEVMODE
6376
6653
  );
6377
6654
  });
@@ -6380,7 +6657,16 @@ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
6380
6657
  var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
6381
6658
  function useDevChecksImpl(props) {
6382
6659
  const ctx = useStateContext();
6383
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
6660
+ const { anchoredEndSpace, childrenMode, keyExtractor, numColumns, renderScrollComponent, useWindowScroll } = props;
6661
+ const hasAnchoredEndSpace = !!anchoredEndSpace;
6662
+ useEffect(() => {
6663
+ if (hasAnchoredEndSpace && (numColumns != null ? numColumns : 1) > 1) {
6664
+ warnDevOnce(
6665
+ "anchoredEndSpaceNumColumns",
6666
+ "anchoredEndSpace is only supported when numColumns is 1. Using it with multiple columns may produce incorrect anchored spacing."
6667
+ );
6668
+ }
6669
+ }, [hasAnchoredEndSpace, numColumns]);
6384
6670
  useEffect(() => {
6385
6671
  if (useWindowScroll && renderScrollComponent) {
6386
6672
  warnDevOnce(
@@ -6446,6 +6732,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6446
6732
  if (didColumnsChange) {
6447
6733
  state.sizes.clear();
6448
6734
  state.sizesKnown.clear();
6735
+ invalidateContainerFixedItemSizes(state);
6449
6736
  for (const key in state.averageSizes) {
6450
6737
  delete state.averageSizes[key];
6451
6738
  }
@@ -6692,7 +6979,7 @@ function onScroll(ctx, event) {
6692
6979
  }
6693
6980
  }
6694
6981
  state.scrollPending = newScroll;
6695
- updateScroll(ctx, newScroll, insetChanged);
6982
+ updateScroll(ctx, newScroll, insetChanged, { fromNativeScrollEvent: true });
6696
6983
  trackInitialScrollNativeProgress(state, newScroll);
6697
6984
  clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
6698
6985
  if (state.scrollingTo) {
@@ -6707,6 +6994,59 @@ function onScroll(ctx, event) {
6707
6994
  }
6708
6995
  }
6709
6996
 
6997
+ // src/core/ScheduledWork.ts
6998
+ var ScheduledWork = class {
6999
+ constructor() {
7000
+ this.work = /* @__PURE__ */ new Map();
7001
+ }
7002
+ timeout(callback, delay, key) {
7003
+ if (key) {
7004
+ this.cancel(key);
7005
+ }
7006
+ const work = [void 0, clearTimeout];
7007
+ const handle = setTimeout(() => {
7008
+ const workKey = key != null ? key : handle;
7009
+ if (this.work.get(workKey) === work) {
7010
+ this.work.delete(workKey);
7011
+ callback();
7012
+ }
7013
+ }, delay);
7014
+ work[0] = handle;
7015
+ this.work.set(key != null ? key : handle, work);
7016
+ }
7017
+ frame(callback, key) {
7018
+ this.cancel(key);
7019
+ const work = [void 0, cancelAnimationFrame];
7020
+ this.work.set(key, work);
7021
+ work[0] = requestAnimationFrame(() => {
7022
+ if (this.work.get(key) === work) {
7023
+ this.work.delete(key);
7024
+ callback();
7025
+ }
7026
+ });
7027
+ }
7028
+ register(key, cancel) {
7029
+ this.cancel(key);
7030
+ this.work.set(key, [void 0, cancel]);
7031
+ }
7032
+ cancel(key) {
7033
+ const work = this.work.get(key);
7034
+ if (work) {
7035
+ this.work.delete(key);
7036
+ work[1](work[0]);
7037
+ }
7038
+ }
7039
+ has(key) {
7040
+ return this.work.has(key);
7041
+ }
7042
+ dispose() {
7043
+ for (const [handle, cancel] of this.work.values()) {
7044
+ cancel(handle);
7045
+ }
7046
+ this.work.clear();
7047
+ }
7048
+ };
7049
+
6710
7050
  // src/core/ScrollAdjustHandler.ts
6711
7051
  var ScrollAdjustHandler = class {
6712
7052
  constructor(ctx) {
@@ -6855,8 +7195,8 @@ function getAverageItemSizes(state) {
6855
7195
  return averageItemSizes;
6856
7196
  }
6857
7197
  function triggerMountedContainerLayouts(ctx) {
6858
- for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6859
- triggerLayout();
7198
+ {
7199
+ scheduleContainerLayout(ctx);
6860
7200
  }
6861
7201
  }
6862
7202
  function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
@@ -6864,7 +7204,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6864
7204
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
6865
7205
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
6866
7206
  let imperativeScrollToken = 0;
6867
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
7207
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.scheduledWork.has("mvcpRecalculate") || state.ignoreScrollFromMVCP !== void 0;
6868
7208
  const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
6869
7209
  var _a3;
6870
7210
  const props = state.props;
@@ -6898,9 +7238,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6898
7238
  run();
6899
7239
  return;
6900
7240
  }
6901
- requestAnimationFrame(check);
7241
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6902
7242
  };
6903
- requestAnimationFrame(check);
7243
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6904
7244
  };
6905
7245
  const runScrollRequest = (token, resolve, run, isReady = () => true) => {
6906
7246
  const runNow = () => {
@@ -6922,10 +7262,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6922
7262
  }
6923
7263
  };
6924
7264
  const startImperativeScroll = (resolve) => {
6925
- var _a3;
7265
+ state.scheduledWork.cancel("imperativeScrollReady");
6926
7266
  const token = ++imperativeScrollToken;
6927
- state.pendingScrollToEnd = void 0;
6928
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
7267
+ settlePendingImperativeScroll(state);
6929
7268
  state.pendingScrollResolve = resolve;
6930
7269
  return token;
6931
7270
  };
@@ -6969,6 +7308,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6969
7308
  const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
6970
7309
  state.sizes.clear();
6971
7310
  state.sizesKnown.clear();
7311
+ invalidateContainerFixedItemSizes(state);
6972
7312
  for (const key in state.averageSizes) {
6973
7313
  delete state.averageSizes[key];
6974
7314
  }
@@ -6990,6 +7330,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6990
7330
  return {
6991
7331
  clearCaches,
6992
7332
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
7333
+ getAnimatableRef: () => {
7334
+ var _a3, _b, _c;
7335
+ return (_c = (_b = (_a3 = refScroller.current).getNativeScrollRef) == null ? void 0 : _b.call(_a3)) != null ? _c : refScroller.current;
7336
+ },
6993
7337
  getNativeScrollRef: () => refScroller.current,
6994
7338
  getScrollableNode: () => refScroller.current.getScrollableNode(),
6995
7339
  getScrollResponder: () => refScroller.current.getScrollResponder(),
@@ -7004,6 +7348,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7004
7348
  end: state.endNoBuffer,
7005
7349
  endBuffered: state.endBuffered,
7006
7350
  getAverageItemSizes: () => getAverageItemSizes(state),
7351
+ indexByKey: (key) => state.indexByKey.get(key),
7007
7352
  isAtEnd: peek$(ctx, "isAtEnd"),
7008
7353
  isAtStart: peek$(ctx, "isAtStart"),
7009
7354
  isEndReached: state.isEndReached,
@@ -7307,7 +7652,7 @@ var LegendList = typedMemo(
7307
7652
  })
7308
7653
  );
7309
7654
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
7310
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
7655
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
7311
7656
  const noopOnScroll = useCallback((_event) => {
7312
7657
  }, []);
7313
7658
  if (props.recycleItems === void 0) {
@@ -7360,6 +7705,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7360
7705
  onMomentumScrollEnd,
7361
7706
  onRefresh,
7362
7707
  onScroll: onScrollProp,
7708
+ onScrollBeginDrag,
7363
7709
  onStartReached,
7364
7710
  onStartReachedThreshold = 0.5,
7365
7711
  onStickyHeaderChange,
@@ -7382,9 +7728,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7382
7728
  ...rest
7383
7729
  } = props;
7384
7730
  const animatedPropsInternal = props.animatedPropsInternal;
7731
+ const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";
7385
7732
  const positionComponentInternal = props.positionComponentInternal;
7386
7733
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
7387
7734
  const {
7735
+ anchoredEndSpaceOwnerInternal: _anchoredEndSpaceOwnerInternal,
7388
7736
  positionComponentInternal: _positionComponentInternal,
7389
7737
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
7390
7738
  ...restProps
@@ -7406,6 +7754,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7406
7754
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
7407
7755
  const stylePaddingLeftState = extractPadding(style, contentContainerStyle, "Left");
7408
7756
  const stylePaddingRightState = extractPadding(style, contentContainerStyle, "Right");
7757
+ const stylePaddingEndState = getStylePaddingEnd({
7758
+ horizontal,
7759
+ rtl,
7760
+ stylePaddingBottom: stylePaddingBottomState,
7761
+ stylePaddingLeft: stylePaddingLeftState,
7762
+ stylePaddingRight: stylePaddingRightState
7763
+ });
7409
7764
  const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
7410
7765
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
7411
7766
  maintainVisibleContentPositionProp
@@ -7418,13 +7773,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7418
7773
  const initialScrollProp = initialScrollAtEnd ? {
7419
7774
  index: Math.max(0, dataProp.length - 1),
7420
7775
  preserveForBottomPadding: true,
7421
- viewOffset: -stylePaddingBottomState,
7776
+ viewOffset: -stylePaddingEndState,
7422
7777
  viewPosition: 1
7423
7778
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
7424
- index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
7779
+ index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
7425
7780
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
7426
- viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
7427
- viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
7781
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingEndState : 0,
7782
+ viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
7428
7783
  } : {
7429
7784
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
7430
7785
  viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
@@ -7437,7 +7792,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7437
7792
  const [, scheduleImperativeScrollCommit] = React3.useReducer((value) => value + 1, 0);
7438
7793
  const ctx = useStateContext();
7439
7794
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
7440
- 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;
7795
+ 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;
7441
7796
  const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
7442
7797
  const refScroller = useRef(null);
7443
7798
  const combinedRef = useCombinedRef(refScroller, refScrollView);
@@ -7452,8 +7807,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7452
7807
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
7453
7808
  alwaysRender == null ? void 0 : alwaysRender.top,
7454
7809
  alwaysRender == null ? void 0 : alwaysRender.bottom,
7455
- (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
7456
- (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
7810
+ (_k = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _k.join(","),
7811
+ (_l = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _l.join(","),
7457
7812
  dataProp,
7458
7813
  dataKey,
7459
7814
  dataVersion,
@@ -7466,13 +7821,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7466
7821
  if (!refState.current) {
7467
7822
  if (!ctx.state) {
7468
7823
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
7469
- ctx.values.set("adaptiveRender", (_l = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _l : "normal");
7824
+ ctx.values.set("adaptiveRender", (_m = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _m : "normal");
7470
7825
  ctx.state = {
7471
7826
  averageSizes: {},
7472
7827
  columnSpans: [],
7473
7828
  columns: [],
7829
+ containerItemGenerations: [],
7474
7830
  containerItemKeys: /* @__PURE__ */ new Map(),
7475
- containerItemTypes: /* @__PURE__ */ new Map(),
7831
+ containerItemMetadata: /* @__PURE__ */ new Map(),
7476
7832
  contentInsetOverride: void 0,
7477
7833
  dataChangeEpoch: 0,
7478
7834
  dataChangeNeedsScrollUpdate: false,
@@ -7484,6 +7840,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7484
7840
  endNoBuffer: -1,
7485
7841
  endReachedSnapshot: void 0,
7486
7842
  firstFullyOnScreenIndex: -1,
7843
+ freshDataTransitionEpoch: 0,
7487
7844
  hasHadNonEmptyData: dataProp.length > 0,
7488
7845
  idCache: [],
7489
7846
  idsInView: [],
@@ -7508,8 +7865,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7508
7865
  positions: [],
7509
7866
  props: {},
7510
7867
  queuedCalculateItemsInView: 0,
7511
- queuedFullDrawDistancePrewarm: void 0,
7512
7868
  refScroller: { current: null },
7869
+ scheduledWork: new ScheduledWork(),
7513
7870
  scroll: 0,
7514
7871
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
7515
7872
  scrollForNextCalculateItemsInView: void 0,
@@ -7525,11 +7882,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7525
7882
  startBuffered: -1,
7526
7883
  startNoBuffer: -1,
7527
7884
  startReachedSnapshot: void 0,
7528
- startReachedSnapshotDataChangeEpoch: void 0,
7529
7885
  stickyContainerPool: /* @__PURE__ */ new Set(),
7530
7886
  stickyContainers: /* @__PURE__ */ new Map(),
7531
- timeoutAdaptiveRender: void 0,
7532
- timeouts: /* @__PURE__ */ new Set(),
7533
7887
  totalSize: 0,
7534
7888
  viewabilityConfigCallbackPairs: void 0
7535
7889
  };
@@ -7551,13 +7905,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7551
7905
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
7552
7906
  ctx.scrollAxisGap = nextScrollAxisGap;
7553
7907
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
7554
- const previousDataLength = (_n = (_m = state.props.data) == null ? void 0 : _m.length) != null ? _n : 0;
7908
+ const previousDataLength = (_o = (_n = state.props.data) == null ? void 0 : _n.length) != null ? _o : 0;
7555
7909
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
7556
7910
  const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
7557
7911
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
7558
7912
  const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
7559
7913
  const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
7560
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_o = state.initialScroll) == null ? void 0 : _o.viewPosition) === 1 && state.props.data.length > 0) {
7914
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_p = state.initialScroll) == null ? void 0 : _p.viewPosition) === 1 && state.props.data.length > 0) {
7561
7915
  clearPreservedInitialScrollTarget(state);
7562
7916
  }
7563
7917
  if (didDataChangeLocal) {
@@ -7566,10 +7920,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7566
7920
  state.didDataChange = true;
7567
7921
  state.previousData = state.props.data;
7568
7922
  }
7923
+ if (shouldResetFreshDataLayout) {
7924
+ state.freshDataTransitionEpoch += 1;
7925
+ }
7569
7926
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
7570
7927
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
7571
- const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
7572
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_p = state.props.anchoredEndSpace) == null ? void 0 : _p.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
7928
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_q = state.props.anchoredEndSpace) == null ? void 0 : _q.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
7573
7929
  state.props = {
7574
7930
  adaptiveRender: experimental_adaptiveRender,
7575
7931
  alignItemsAtEnd,
@@ -7577,7 +7933,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7577
7933
  alwaysRender,
7578
7934
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
7579
7935
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
7580
- anchoredEndSpace: anchoredEndSpaceResolved,
7936
+ anchoredEndSpace,
7937
+ anchoredEndSpaceOwner,
7581
7938
  animatedProps: animatedPropsInternal,
7582
7939
  contentContainerAlignItems: contentContainerStyle.alignItems,
7583
7940
  contentInset,
@@ -7601,7 +7958,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7601
7958
  onFirstVisibleItemChanged,
7602
7959
  onItemSizeChanged,
7603
7960
  onLoad,
7961
+ onMomentumScrollEnd,
7604
7962
  onScroll: throttleScrollFn,
7963
+ onScrollBeginDrag,
7605
7964
  onStartReached,
7606
7965
  onStartReachedThreshold,
7607
7966
  onStickyHeaderChange,
@@ -7624,12 +7983,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7624
7983
  if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
7625
7984
  resetAdaptiveRender(ctx);
7626
7985
  }
7627
- if (shouldResetFreshDataLayout) {
7628
- resetInitialRenderState(ctx, {
7629
- resetInitialScroll: !!initialScrollProp,
7630
- resetLayout: true
7631
- });
7632
- }
7633
7986
  const memoizedLastItemKeys = useMemo(() => {
7634
7987
  if (!dataProp.length) return [];
7635
7988
  return Array.from(
@@ -7688,6 +8041,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7688
8041
  useDevChecks(props);
7689
8042
  }
7690
8043
  useLayoutEffect(() => {
8044
+ if (shouldResetFreshDataLayout) {
8045
+ resetInitialRenderState(ctx, {
8046
+ resetInitialScroll: !!initialScrollProp,
8047
+ resetLayout: true
8048
+ });
8049
+ }
7691
8050
  handleInitialScrollDataChange(ctx, {
7692
8051
  dataLength: dataProp.length,
7693
8052
  didDataChange: didDataChangeLocal,
@@ -7695,7 +8054,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7695
8054
  initialScrollAtEnd,
7696
8055
  latestInitialScroll: initialScrollProp,
7697
8056
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
7698
- stylePaddingBottom: stylePaddingBottomState,
8057
+ stylePaddingEnd: stylePaddingEndState,
7699
8058
  useBootstrapInitialScroll: usesBootstrapInitialScroll
7700
8059
  });
7701
8060
  }, [
@@ -7704,7 +8063,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7704
8063
  didDataChangeLocal,
7705
8064
  shouldResetFreshDataLayout,
7706
8065
  initialScrollAtEnd,
7707
- stylePaddingBottomState,
8066
+ stylePaddingEndState,
7708
8067
  usesBootstrapInitialScroll
7709
8068
  ]);
7710
8069
  useLayoutEffect(() => {
@@ -7722,7 +8081,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7722
8081
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
7723
8082
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
7724
8083
  didAnchoredEndSpaceAnchorIndexChange,
7725
- numColumnsProp
8084
+ horizontal,
8085
+ numColumnsProp,
8086
+ rtl,
8087
+ stylePaddingBottomState,
8088
+ stylePaddingLeftState,
8089
+ stylePaddingRightState
7726
8090
  ]);
7727
8091
  useLayoutEffect(() => {
7728
8092
  const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
@@ -7738,10 +8102,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7738
8102
  dataLength: dataProp.length,
7739
8103
  footerSize: layout[horizontal ? "width" : "height"],
7740
8104
  initialScrollAtEnd,
7741
- stylePaddingBottom: stylePaddingBottomState
8105
+ stylePaddingEnd: stylePaddingEndState
7742
8106
  });
7743
8107
  },
7744
- [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
8108
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingEndState, usesBootstrapInitialScroll]
7745
8109
  );
7746
8110
  const onLayoutChange = useCallback(
7747
8111
  (layout, fromLayoutEffect) => {
@@ -7758,7 +8122,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7758
8122
  }
7759
8123
  advanceCurrentInitialScrollSession(ctx);
7760
8124
  },
7761
- [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
8125
+ [dataProp.length, initialScrollAtEnd, stylePaddingEndState, usesBootstrapInitialScroll]
7762
8126
  );
7763
8127
  const { onLayout } = useOnLayoutSync({
7764
8128
  onLayoutChange,
@@ -7851,14 +8215,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7851
8215
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
7852
8216
  useEffect(() => {
7853
8217
  return () => {
7854
- if (state.queuedFullDrawDistancePrewarm !== void 0) {
7855
- cancelAnimationFrame(state.queuedFullDrawDistancePrewarm);
7856
- state.queuedFullDrawDistancePrewarm = void 0;
7857
- }
7858
- for (const timeout of state.timeouts) {
7859
- clearTimeout(timeout);
7860
- }
7861
- state.timeouts.clear();
8218
+ cancelImperativeScroll(state);
8219
+ state.scheduledWork.dispose();
7862
8220
  };
7863
8221
  }, [state]);
7864
8222
  useLayoutEffect(() => {
@@ -7876,11 +8234,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7876
8234
  getRenderedItem: (key) => getRenderedItem(ctx, key),
7877
8235
  onMomentumScrollEnd: (event) => {
7878
8236
  checkFinishedScrollFallback(ctx);
7879
- if (onMomentumScrollEnd) {
7880
- onMomentumScrollEnd(event);
8237
+ if (state.props.onMomentumScrollEnd) {
8238
+ state.props.onMomentumScrollEnd(event);
7881
8239
  }
7882
8240
  },
7883
- onScroll: (event) => onScroll(ctx, event)
8241
+ onScroll: (event) => onScroll(ctx, event),
8242
+ onScrollBeginDrag: (event) => {
8243
+ var _a4, _b2;
8244
+ prepareReachedEdgeForNextUserScroll(ctx);
8245
+ (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
8246
+ },
8247
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
7884
8248
  }),
7885
8249
  []
7886
8250
  );
@@ -7894,6 +8258,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7894
8258
  canRender,
7895
8259
  contentContainerStyle,
7896
8260
  contentInset,
8261
+ freshDataTransitionEpoch: state.freshDataTransitionEpoch,
7897
8262
  getRenderedItem: fns.getRenderedItem,
7898
8263
  horizontal,
7899
8264
  initialContentOffset,
@@ -7901,13 +8266,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7901
8266
  ListFooterComponent,
7902
8267
  ListFooterComponentStyle,
7903
8268
  ListHeaderComponent,
8269
+ onInternalScrollBeginDrag: fns.onScrollBeginDrag,
8270
+ onInternalScrollEnd: fns.onScrollEnd,
7904
8271
  onLayout,
7905
8272
  onLayoutFooter,
7906
8273
  onMomentumScrollEnd: fns.onMomentumScrollEnd,
7907
8274
  onScroll: onScrollHandler,
7908
8275
  recycleItems,
7909
8276
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControlElement, {
7910
- progressViewOffset: ((_q = refreshControlElement.props.progressViewOffset) != null ? _q : 0) + stylePaddingTopState
8277
+ progressViewOffset: ((_r = refreshControlElement.props.progressViewOffset) != null ? _r : 0) + stylePaddingTopState
7911
8278
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3.createElement(
7912
8279
  RefreshControl,
7913
8280
  {
@@ -7918,7 +8285,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7918
8285
  ),
7919
8286
  refScrollView: combinedRef,
7920
8287
  renderScrollComponent,
7921
- scrollAdjustHandler: (_r = refState.current) == null ? void 0 : _r.scrollAdjustHandler,
8288
+ scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
7922
8289
  scrollEventThrottle: 0,
7923
8290
  snapToIndices,
7924
8291
  stickyHeaderIndices,