@legendapp/list 2.1.0-beta.10 → 2.1.0-beta.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -105,11 +105,12 @@ function set$(ctx, signalName, value) {
105
105
  }
106
106
  }
107
107
  function getContentSize(ctx) {
108
+ var _a3, _b;
108
109
  const { values } = ctx;
109
110
  const stylePaddingTop = values.get("stylePaddingTop") || 0;
110
111
  const headerSize = values.get("headerSize") || 0;
111
112
  const footerSize = values.get("footerSize") || 0;
112
- const totalSize = values.get("totalSize");
113
+ const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
113
114
  return headerSize + footerSize + totalSize + stylePaddingTop;
114
115
  }
115
116
  function useArr$(signalNames) {
@@ -1220,44 +1221,28 @@ function updateAlignItemsPaddingTop(ctx, state) {
1220
1221
  }
1221
1222
  }
1222
1223
 
1223
- // src/core/updateTotalSize.ts
1224
- function updateTotalSize(ctx, state) {
1225
- const {
1226
- positions,
1227
- props: { data }
1228
- } = state;
1229
- if (data.length === 0) {
1230
- addTotalSize(ctx, state, null, 0);
1231
- } else {
1232
- const lastId = getId(state, data.length - 1);
1233
- if (lastId !== void 0) {
1234
- const lastPosition = positions.get(lastId);
1235
- if (lastPosition !== void 0) {
1236
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1237
- if (lastSize !== void 0) {
1238
- const totalSize = lastPosition + lastSize;
1239
- addTotalSize(ctx, state, null, totalSize);
1240
- }
1241
- }
1242
- }
1243
- }
1244
- }
1224
+ // src/core/addTotalSize.ts
1245
1225
  function addTotalSize(ctx, state, key, add) {
1246
1226
  const { alignItemsAtEnd } = state.props;
1247
1227
  const prevTotalSize = state.totalSize;
1228
+ let totalSize = state.totalSize;
1248
1229
  if (key === null) {
1249
- state.totalSize = add;
1230
+ totalSize = add;
1250
1231
  if (state.timeoutSetPaddingTop) {
1251
1232
  clearTimeout(state.timeoutSetPaddingTop);
1252
1233
  state.timeoutSetPaddingTop = void 0;
1253
1234
  }
1254
1235
  } else {
1255
- state.totalSize += add;
1236
+ totalSize += add;
1256
1237
  }
1257
- if (prevTotalSize !== state.totalSize) {
1258
- set$(ctx, "totalSize", state.totalSize);
1259
- if (alignItemsAtEnd) {
1260
- updateAlignItemsPaddingTop(ctx, state);
1238
+ if (prevTotalSize !== totalSize) {
1239
+ {
1240
+ state.pendingTotalSize = void 0;
1241
+ state.totalSize = totalSize;
1242
+ set$(ctx, "totalSize", totalSize);
1243
+ if (alignItemsAtEnd) {
1244
+ updateAlignItemsPaddingTop(ctx, state);
1245
+ }
1261
1246
  }
1262
1247
  }
1263
1248
  }
@@ -1462,6 +1447,7 @@ function onScroll(ctx, state, event) {
1462
1447
  onScrollProp == null ? void 0 : onScrollProp(event);
1463
1448
  }
1464
1449
  function updateScroll(ctx, state, newScroll, forceUpdate) {
1450
+ var _a3;
1465
1451
  const scrollingTo = peek$(ctx, "scrollingTo");
1466
1452
  state.hasScrolled = true;
1467
1453
  state.lastBatchingAction = Date.now();
@@ -1495,7 +1481,7 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1495
1481
  }
1496
1482
  if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1497
1483
  state.ignoreScrollFromMVCPIgnored = false;
1498
- calculateItemsInView(ctx, state, { doMVCP: scrollingTo !== void 0 });
1484
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1499
1485
  checkAtBottom(ctx, state);
1500
1486
  checkAtTop(state);
1501
1487
  state.dataChangeNeedsScrollUpdate = false;
@@ -1508,7 +1494,11 @@ function finishScrollTo(ctx, state) {
1508
1494
  if (state) {
1509
1495
  state.scrollHistory.length = 0;
1510
1496
  state.initialScroll = void 0;
1497
+ state.initialAnchor = void 0;
1511
1498
  set$(ctx, "scrollingTo", void 0);
1499
+ if (state.pendingTotalSize !== void 0) {
1500
+ addTotalSize(ctx, state, null, state.pendingTotalSize);
1501
+ }
1512
1502
  if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1513
1503
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1514
1504
  }
@@ -1543,7 +1533,14 @@ function scrollTo(ctx, state, params) {
1543
1533
  }
1544
1534
  if (!animated) {
1545
1535
  state.scroll = offset;
1546
- setTimeout(() => finishScrollTo(ctx, state), 100);
1536
+ {
1537
+ const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1538
+ if (value) {
1539
+ finishScrollTo(ctx, state);
1540
+ unlisten();
1541
+ }
1542
+ });
1543
+ }
1547
1544
  if (isInitialScroll) {
1548
1545
  setTimeout(() => {
1549
1546
  state.initialScroll = void 0;
@@ -1571,6 +1568,58 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1571
1568
  }
1572
1569
  }
1573
1570
 
1571
+ // src/core/ensureInitialAnchor.ts
1572
+ var INITIAL_ANCHOR_TOLERANCE = 0.5;
1573
+ var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1574
+ var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1575
+ function ensureInitialAnchor(ctx, state) {
1576
+ var _a3, _b, _c, _d, _e;
1577
+ const anchor = state.initialAnchor;
1578
+ const item = state.props.data[anchor.index];
1579
+ const containersDidLayout = peek$(ctx, "containersDidLayout");
1580
+ if (!containersDidLayout) {
1581
+ return;
1582
+ }
1583
+ const id = getId(state, anchor.index);
1584
+ if (state.positions.get(id) === void 0) {
1585
+ return;
1586
+ }
1587
+ const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1588
+ if (size === void 0) {
1589
+ return;
1590
+ }
1591
+ const availableSpace = Math.max(0, state.scrollLength - size);
1592
+ const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1593
+ const contentSize = getContentSize(ctx);
1594
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1595
+ const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1596
+ const delta = clampedDesiredOffset - state.scroll;
1597
+ if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1598
+ const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1599
+ if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1600
+ state.initialAnchor = void 0;
1601
+ } else {
1602
+ anchor.settledTicks = settledTicks;
1603
+ }
1604
+ return;
1605
+ }
1606
+ if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1607
+ state.initialAnchor = void 0;
1608
+ return;
1609
+ }
1610
+ const lastDelta = anchor.lastDelta;
1611
+ if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1612
+ state.initialAnchor = void 0;
1613
+ return;
1614
+ }
1615
+ Object.assign(anchor, {
1616
+ attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1617
+ lastDelta: delta,
1618
+ settledTicks: 0
1619
+ });
1620
+ requestAdjust(ctx, state, delta);
1621
+ }
1622
+
1574
1623
  // src/core/mvcp.ts
1575
1624
  function prepareMVCP(ctx, state, dataChanged) {
1576
1625
  const {
@@ -1619,7 +1668,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1619
1668
  if (targetId !== void 0 && prevPosition !== void 0) {
1620
1669
  const newPosition = positions.get(targetId);
1621
1670
  if (newPosition !== void 0) {
1622
- const totalSize = peek$(ctx, "totalSize");
1671
+ const totalSize = getContentSize(ctx);
1623
1672
  let diff = newPosition - prevPosition;
1624
1673
  if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
1625
1674
  if (diff > 0) {
@@ -1699,6 +1748,29 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1699
1748
  return maxSize;
1700
1749
  }
1701
1750
 
1751
+ // src/core/updateTotalSize.ts
1752
+ function updateTotalSize(ctx, state) {
1753
+ const {
1754
+ positions,
1755
+ props: { data }
1756
+ } = state;
1757
+ if (data.length === 0) {
1758
+ addTotalSize(ctx, state, null, 0);
1759
+ } else {
1760
+ const lastId = getId(state, data.length - 1);
1761
+ if (lastId !== void 0) {
1762
+ const lastPosition = positions.get(lastId);
1763
+ if (lastPosition !== void 0) {
1764
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1765
+ if (lastSize !== void 0) {
1766
+ const totalSize = lastPosition + lastSize;
1767
+ addTotalSize(ctx, state, null, totalSize);
1768
+ }
1769
+ }
1770
+ }
1771
+ }
1772
+ }
1773
+
1702
1774
  // src/utils/getScrollVelocity.ts
1703
1775
  var getScrollVelocity = (state) => {
1704
1776
  const { scrollHistory } = state;
@@ -2308,9 +2380,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2308
2380
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2309
2381
  const prevNumContainers = peek$(ctx, "numContainers");
2310
2382
  if (!data || scrollLength === 0 || !prevNumContainers) {
2383
+ if (state.initialAnchor) {
2384
+ ensureInitialAnchor(ctx, state);
2385
+ }
2311
2386
  return;
2312
2387
  }
2313
- const totalSize = peek$(ctx, "totalSize");
2388
+ const totalSize = getContentSize(ctx);
2314
2389
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2315
2390
  const numColumns = peek$(ctx, "numColumns");
2316
2391
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
@@ -2357,6 +2432,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2357
2432
  if (!dataChanged && scrollForNextCalculateItemsInView) {
2358
2433
  const { top, bottom } = scrollForNextCalculateItemsInView;
2359
2434
  if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2435
+ if (state.initialAnchor) {
2436
+ ensureInitialAnchor(ctx, state);
2437
+ }
2360
2438
  return;
2361
2439
  }
2362
2440
  }
@@ -2609,6 +2687,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2609
2687
  }
2610
2688
  }
2611
2689
  });
2690
+ if (state.initialAnchor) {
2691
+ ensureInitialAnchor(ctx, state);
2692
+ }
2612
2693
  }
2613
2694
 
2614
2695
  // src/core/checkActualChange.ts
@@ -3225,7 +3306,7 @@ var LegendList = typedMemo(
3225
3306
  })
3226
3307
  );
3227
3308
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3228
- var _a3;
3309
+ var _a3, _b;
3229
3310
  const {
3230
3311
  alignItemsAtEnd = false,
3231
3312
  columnWrapperStyle,
@@ -3316,6 +3397,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3316
3397
  idCache: [],
3317
3398
  idsInView: [],
3318
3399
  indexByKey: /* @__PURE__ */ new Map(),
3400
+ initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
3401
+ attempts: 0,
3402
+ index: initialScrollProp.index,
3403
+ settledTicks: 0,
3404
+ viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3405
+ viewPosition: initialScrollProp.viewPosition
3406
+ } : void 0,
3319
3407
  initialScroll: initialScrollProp,
3320
3408
  isAtEnd: false,
3321
3409
  isAtStart: false,
@@ -3437,10 +3525,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3437
3525
  );
3438
3526
  }
3439
3527
  const initialContentOffset = useMemo(() => {
3528
+ var _a4, _b2;
3440
3529
  const { initialScroll } = refState.current;
3441
3530
  if (!initialScroll) {
3531
+ refState.current.initialAnchor = void 0;
3442
3532
  return 0;
3443
3533
  }
3534
+ if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3535
+ refState.current.initialAnchor = {
3536
+ attempts: 0,
3537
+ index: initialScroll.index,
3538
+ settledTicks: 0,
3539
+ viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3540
+ viewPosition: initialScroll.viewPosition
3541
+ };
3542
+ }
3444
3543
  if (initialScroll.contentOffset !== void 0) {
3445
3544
  return initialScroll.contentOffset;
3446
3545
  }
@@ -3490,6 +3589,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3490
3589
  scrollTo(ctx, state, {
3491
3590
  animated: false,
3492
3591
  index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3592
+ isInitialScroll: true,
3493
3593
  offset: initialContentOffset,
3494
3594
  precomputedWithViewOffset: true
3495
3595
  });
@@ -3595,7 +3695,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3595
3695
  }
3596
3696
  ),
3597
3697
  refScrollView: combinedRef,
3598
- scrollAdjustHandler: (_a3 = refState.current) == null ? void 0 : _a3.scrollAdjustHandler,
3698
+ scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3599
3699
  scrollEventThrottle: 16 ,
3600
3700
  snapToIndices,
3601
3701
  stickyIndices,
@@ -395,12 +395,14 @@ interface InternalState {
395
395
  refScroller: React.RefObject<ScrollView>;
396
396
  loadStartTime: number;
397
397
  initialScroll: ScrollIndexWithOffsetAndContentOffset | undefined;
398
+ initialAnchor?: InitialScrollAnchor;
398
399
  lastLayout: LayoutRectangle | undefined;
399
400
  timeoutSetPaddingTop?: any;
400
401
  activeStickyIndex: number | undefined;
401
402
  stickyContainers: Map<number, number>;
402
403
  stickyContainerPool: Set<number>;
403
404
  scrollProcessingEnabled: boolean;
405
+ pendingTotalSize?: number;
404
406
  props: {
405
407
  alignItemsAtEnd: boolean;
406
408
  data: readonly any[];
@@ -638,14 +640,20 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
638
640
  declare const typedMemo: TypedMemo;
639
641
  interface ScrollIndexWithOffset {
640
642
  index: number;
641
- viewOffset: number;
643
+ viewOffset?: number;
644
+ viewPosition?: number;
642
645
  }
643
646
  interface ScrollIndexWithOffsetPosition extends ScrollIndexWithOffset {
644
- viewPosition: number;
647
+ viewPosition?: number;
645
648
  }
646
- interface ScrollIndexWithOffsetAndContentOffset extends ScrollIndexWithOffset {
649
+ interface ScrollIndexWithOffsetAndContentOffset extends ScrollIndexWithOffsetPosition {
647
650
  contentOffset?: number;
648
651
  }
652
+ interface InitialScrollAnchor extends ScrollIndexWithOffsetPosition {
653
+ attempts?: number;
654
+ lastDelta?: number;
655
+ settledTicks?: number;
656
+ }
649
657
  type GetRenderedItemResult<ItemT> = {
650
658
  index: number;
651
659
  item: ItemT;
@@ -668,4 +676,4 @@ declare function useListScrollSize(): {
668
676
  };
669
677
  declare function useSyncLayout(): () => void;
670
678
 
671
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
679
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InitialScrollAnchor, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
package/index.native.d.ts CHANGED
@@ -395,12 +395,14 @@ interface InternalState {
395
395
  refScroller: React.RefObject<ScrollView>;
396
396
  loadStartTime: number;
397
397
  initialScroll: ScrollIndexWithOffsetAndContentOffset | undefined;
398
+ initialAnchor?: InitialScrollAnchor;
398
399
  lastLayout: LayoutRectangle | undefined;
399
400
  timeoutSetPaddingTop?: any;
400
401
  activeStickyIndex: number | undefined;
401
402
  stickyContainers: Map<number, number>;
402
403
  stickyContainerPool: Set<number>;
403
404
  scrollProcessingEnabled: boolean;
405
+ pendingTotalSize?: number;
404
406
  props: {
405
407
  alignItemsAtEnd: boolean;
406
408
  data: readonly any[];
@@ -638,14 +640,20 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
638
640
  declare const typedMemo: TypedMemo;
639
641
  interface ScrollIndexWithOffset {
640
642
  index: number;
641
- viewOffset: number;
643
+ viewOffset?: number;
644
+ viewPosition?: number;
642
645
  }
643
646
  interface ScrollIndexWithOffsetPosition extends ScrollIndexWithOffset {
644
- viewPosition: number;
647
+ viewPosition?: number;
645
648
  }
646
- interface ScrollIndexWithOffsetAndContentOffset extends ScrollIndexWithOffset {
649
+ interface ScrollIndexWithOffsetAndContentOffset extends ScrollIndexWithOffsetPosition {
647
650
  contentOffset?: number;
648
651
  }
652
+ interface InitialScrollAnchor extends ScrollIndexWithOffsetPosition {
653
+ attempts?: number;
654
+ lastDelta?: number;
655
+ settledTicks?: number;
656
+ }
649
657
  type GetRenderedItemResult<ItemT> = {
650
658
  index: number;
651
659
  item: ItemT;
@@ -668,4 +676,4 @@ declare function useListScrollSize(): {
668
676
  };
669
677
  declare function useSyncLayout(): () => void;
670
678
 
671
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
679
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InitialScrollAnchor, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };