@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.d.mts 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 };
package/index.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 };
package/index.js CHANGED
@@ -126,11 +126,12 @@ function set$(ctx, signalName, value) {
126
126
  }
127
127
  }
128
128
  function getContentSize(ctx) {
129
+ var _a3, _b;
129
130
  const { values } = ctx;
130
131
  const stylePaddingTop = values.get("stylePaddingTop") || 0;
131
132
  const headerSize = values.get("headerSize") || 0;
132
133
  const footerSize = values.get("footerSize") || 0;
133
- const totalSize = values.get("totalSize");
134
+ const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
134
135
  return headerSize + footerSize + totalSize + stylePaddingTop;
135
136
  }
136
137
  function useArr$(signalNames) {
@@ -1241,44 +1242,28 @@ function updateAlignItemsPaddingTop(ctx, state) {
1241
1242
  }
1242
1243
  }
1243
1244
 
1244
- // src/core/updateTotalSize.ts
1245
- function updateTotalSize(ctx, state) {
1246
- const {
1247
- positions,
1248
- props: { data }
1249
- } = state;
1250
- if (data.length === 0) {
1251
- addTotalSize(ctx, state, null, 0);
1252
- } else {
1253
- const lastId = getId(state, data.length - 1);
1254
- if (lastId !== void 0) {
1255
- const lastPosition = positions.get(lastId);
1256
- if (lastPosition !== void 0) {
1257
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1258
- if (lastSize !== void 0) {
1259
- const totalSize = lastPosition + lastSize;
1260
- addTotalSize(ctx, state, null, totalSize);
1261
- }
1262
- }
1263
- }
1264
- }
1265
- }
1245
+ // src/core/addTotalSize.ts
1266
1246
  function addTotalSize(ctx, state, key, add) {
1267
1247
  const { alignItemsAtEnd } = state.props;
1268
1248
  const prevTotalSize = state.totalSize;
1249
+ let totalSize = state.totalSize;
1269
1250
  if (key === null) {
1270
- state.totalSize = add;
1251
+ totalSize = add;
1271
1252
  if (state.timeoutSetPaddingTop) {
1272
1253
  clearTimeout(state.timeoutSetPaddingTop);
1273
1254
  state.timeoutSetPaddingTop = void 0;
1274
1255
  }
1275
1256
  } else {
1276
- state.totalSize += add;
1257
+ totalSize += add;
1277
1258
  }
1278
- if (prevTotalSize !== state.totalSize) {
1279
- set$(ctx, "totalSize", state.totalSize);
1280
- if (alignItemsAtEnd) {
1281
- updateAlignItemsPaddingTop(ctx, state);
1259
+ if (prevTotalSize !== totalSize) {
1260
+ {
1261
+ state.pendingTotalSize = void 0;
1262
+ state.totalSize = totalSize;
1263
+ set$(ctx, "totalSize", totalSize);
1264
+ if (alignItemsAtEnd) {
1265
+ updateAlignItemsPaddingTop(ctx, state);
1266
+ }
1282
1267
  }
1283
1268
  }
1284
1269
  }
@@ -1483,6 +1468,7 @@ function onScroll(ctx, state, event) {
1483
1468
  onScrollProp == null ? void 0 : onScrollProp(event);
1484
1469
  }
1485
1470
  function updateScroll(ctx, state, newScroll, forceUpdate) {
1471
+ var _a3;
1486
1472
  const scrollingTo = peek$(ctx, "scrollingTo");
1487
1473
  state.hasScrolled = true;
1488
1474
  state.lastBatchingAction = Date.now();
@@ -1516,7 +1502,7 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1516
1502
  }
1517
1503
  if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1518
1504
  state.ignoreScrollFromMVCPIgnored = false;
1519
- calculateItemsInView(ctx, state, { doMVCP: scrollingTo !== void 0 });
1505
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1520
1506
  checkAtBottom(ctx, state);
1521
1507
  checkAtTop(state);
1522
1508
  state.dataChangeNeedsScrollUpdate = false;
@@ -1529,7 +1515,11 @@ function finishScrollTo(ctx, state) {
1529
1515
  if (state) {
1530
1516
  state.scrollHistory.length = 0;
1531
1517
  state.initialScroll = void 0;
1518
+ state.initialAnchor = void 0;
1532
1519
  set$(ctx, "scrollingTo", void 0);
1520
+ if (state.pendingTotalSize !== void 0) {
1521
+ addTotalSize(ctx, state, null, state.pendingTotalSize);
1522
+ }
1533
1523
  if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1534
1524
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1535
1525
  }
@@ -1564,7 +1554,14 @@ function scrollTo(ctx, state, params) {
1564
1554
  }
1565
1555
  if (!animated) {
1566
1556
  state.scroll = offset;
1567
- setTimeout(() => finishScrollTo(ctx, state), 100);
1557
+ {
1558
+ const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1559
+ if (value) {
1560
+ finishScrollTo(ctx, state);
1561
+ unlisten();
1562
+ }
1563
+ });
1564
+ }
1568
1565
  if (isInitialScroll) {
1569
1566
  setTimeout(() => {
1570
1567
  state.initialScroll = void 0;
@@ -1592,6 +1589,58 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1592
1589
  }
1593
1590
  }
1594
1591
 
1592
+ // src/core/ensureInitialAnchor.ts
1593
+ var INITIAL_ANCHOR_TOLERANCE = 0.5;
1594
+ var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1595
+ var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1596
+ function ensureInitialAnchor(ctx, state) {
1597
+ var _a3, _b, _c, _d, _e;
1598
+ const anchor = state.initialAnchor;
1599
+ const item = state.props.data[anchor.index];
1600
+ const containersDidLayout = peek$(ctx, "containersDidLayout");
1601
+ if (!containersDidLayout) {
1602
+ return;
1603
+ }
1604
+ const id = getId(state, anchor.index);
1605
+ if (state.positions.get(id) === void 0) {
1606
+ return;
1607
+ }
1608
+ const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1609
+ if (size === void 0) {
1610
+ return;
1611
+ }
1612
+ const availableSpace = Math.max(0, state.scrollLength - size);
1613
+ const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1614
+ const contentSize = getContentSize(ctx);
1615
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1616
+ const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1617
+ const delta = clampedDesiredOffset - state.scroll;
1618
+ if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1619
+ const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1620
+ if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1621
+ state.initialAnchor = void 0;
1622
+ } else {
1623
+ anchor.settledTicks = settledTicks;
1624
+ }
1625
+ return;
1626
+ }
1627
+ if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1628
+ state.initialAnchor = void 0;
1629
+ return;
1630
+ }
1631
+ const lastDelta = anchor.lastDelta;
1632
+ if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1633
+ state.initialAnchor = void 0;
1634
+ return;
1635
+ }
1636
+ Object.assign(anchor, {
1637
+ attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1638
+ lastDelta: delta,
1639
+ settledTicks: 0
1640
+ });
1641
+ requestAdjust(ctx, state, delta);
1642
+ }
1643
+
1595
1644
  // src/core/mvcp.ts
1596
1645
  function prepareMVCP(ctx, state, dataChanged) {
1597
1646
  const {
@@ -1640,7 +1689,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1640
1689
  if (targetId !== void 0 && prevPosition !== void 0) {
1641
1690
  const newPosition = positions.get(targetId);
1642
1691
  if (newPosition !== void 0) {
1643
- const totalSize = peek$(ctx, "totalSize");
1692
+ const totalSize = getContentSize(ctx);
1644
1693
  let diff = newPosition - prevPosition;
1645
1694
  if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
1646
1695
  if (diff > 0) {
@@ -1720,6 +1769,29 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1720
1769
  return maxSize;
1721
1770
  }
1722
1771
 
1772
+ // src/core/updateTotalSize.ts
1773
+ function updateTotalSize(ctx, state) {
1774
+ const {
1775
+ positions,
1776
+ props: { data }
1777
+ } = state;
1778
+ if (data.length === 0) {
1779
+ addTotalSize(ctx, state, null, 0);
1780
+ } else {
1781
+ const lastId = getId(state, data.length - 1);
1782
+ if (lastId !== void 0) {
1783
+ const lastPosition = positions.get(lastId);
1784
+ if (lastPosition !== void 0) {
1785
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1786
+ if (lastSize !== void 0) {
1787
+ const totalSize = lastPosition + lastSize;
1788
+ addTotalSize(ctx, state, null, totalSize);
1789
+ }
1790
+ }
1791
+ }
1792
+ }
1793
+ }
1794
+
1723
1795
  // src/utils/getScrollVelocity.ts
1724
1796
  var getScrollVelocity = (state) => {
1725
1797
  const { scrollHistory } = state;
@@ -2329,9 +2401,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2329
2401
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2330
2402
  const prevNumContainers = peek$(ctx, "numContainers");
2331
2403
  if (!data || scrollLength === 0 || !prevNumContainers) {
2404
+ if (state.initialAnchor) {
2405
+ ensureInitialAnchor(ctx, state);
2406
+ }
2332
2407
  return;
2333
2408
  }
2334
- const totalSize = peek$(ctx, "totalSize");
2409
+ const totalSize = getContentSize(ctx);
2335
2410
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2336
2411
  const numColumns = peek$(ctx, "numColumns");
2337
2412
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
@@ -2378,6 +2453,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2378
2453
  if (!dataChanged && scrollForNextCalculateItemsInView) {
2379
2454
  const { top, bottom } = scrollForNextCalculateItemsInView;
2380
2455
  if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2456
+ if (state.initialAnchor) {
2457
+ ensureInitialAnchor(ctx, state);
2458
+ }
2381
2459
  return;
2382
2460
  }
2383
2461
  }
@@ -2630,6 +2708,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2630
2708
  }
2631
2709
  }
2632
2710
  });
2711
+ if (state.initialAnchor) {
2712
+ ensureInitialAnchor(ctx, state);
2713
+ }
2633
2714
  }
2634
2715
 
2635
2716
  // src/core/checkActualChange.ts
@@ -3246,7 +3327,7 @@ var LegendList = typedMemo(
3246
3327
  })
3247
3328
  );
3248
3329
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3249
- var _a3;
3330
+ var _a3, _b;
3250
3331
  const {
3251
3332
  alignItemsAtEnd = false,
3252
3333
  columnWrapperStyle,
@@ -3337,6 +3418,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3337
3418
  idCache: [],
3338
3419
  idsInView: [],
3339
3420
  indexByKey: /* @__PURE__ */ new Map(),
3421
+ initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
3422
+ attempts: 0,
3423
+ index: initialScrollProp.index,
3424
+ settledTicks: 0,
3425
+ viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3426
+ viewPosition: initialScrollProp.viewPosition
3427
+ } : void 0,
3340
3428
  initialScroll: initialScrollProp,
3341
3429
  isAtEnd: false,
3342
3430
  isAtStart: false,
@@ -3458,10 +3546,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3458
3546
  );
3459
3547
  }
3460
3548
  const initialContentOffset = React3.useMemo(() => {
3549
+ var _a4, _b2;
3461
3550
  const { initialScroll } = refState.current;
3462
3551
  if (!initialScroll) {
3552
+ refState.current.initialAnchor = void 0;
3463
3553
  return 0;
3464
3554
  }
3555
+ if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3556
+ refState.current.initialAnchor = {
3557
+ attempts: 0,
3558
+ index: initialScroll.index,
3559
+ settledTicks: 0,
3560
+ viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3561
+ viewPosition: initialScroll.viewPosition
3562
+ };
3563
+ }
3465
3564
  if (initialScroll.contentOffset !== void 0) {
3466
3565
  return initialScroll.contentOffset;
3467
3566
  }
@@ -3511,6 +3610,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3511
3610
  scrollTo(ctx, state, {
3512
3611
  animated: false,
3513
3612
  index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3613
+ isInitialScroll: true,
3514
3614
  offset: initialContentOffset,
3515
3615
  precomputedWithViewOffset: true
3516
3616
  });
@@ -3616,7 +3716,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3616
3716
  }
3617
3717
  ),
3618
3718
  refScrollView: combinedRef,
3619
- scrollAdjustHandler: (_a3 = refState.current) == null ? void 0 : _a3.scrollAdjustHandler,
3719
+ scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3620
3720
  scrollEventThrottle: 16 ,
3621
3721
  snapToIndices,
3622
3722
  stickyIndices,