@legendapp/list 3.0.0-beta.51 → 3.0.0-beta.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.mjs CHANGED
@@ -139,7 +139,10 @@ function useSelector$(signalName, selector) {
139
139
  }
140
140
 
141
141
  // src/state/getContentInsetEnd.ts
142
- function getContentInsetEnd(ctx) {
142
+ function getContentInsetEndAdjustmentEnd(adjustment) {
143
+ return Math.max(0, adjustment != null ? adjustment : 0);
144
+ }
145
+ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
143
146
  var _a3, _b;
144
147
  const state = ctx.state;
145
148
  const { props } = state;
@@ -147,14 +150,21 @@ function getContentInsetEnd(ctx) {
147
150
  const contentInset = props.contentInset;
148
151
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
149
152
  const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
153
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
154
+ contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
155
+ );
150
156
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
151
157
  const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
152
158
  const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
159
+ const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
153
160
  if (overrideInset) {
154
161
  const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
155
- return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
162
+ return Math.max(
163
+ ((horizontal ? mergedInset.right : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
164
+ anchoredEndInset
165
+ );
156
166
  }
157
- return Math.max(baseEndInset, anchoredEndInset);
167
+ return Math.max(adjustedBaseEndInset, anchoredEndInset);
158
168
  }
159
169
 
160
170
  // src/state/getContentSize.ts
@@ -646,6 +656,49 @@ function isInMVCPActiveMode(state) {
646
656
  }
647
657
 
648
658
  // src/components/Container.tsx
659
+ function getContainerPositionStyle({
660
+ columnWrapperStyle,
661
+ horizontal,
662
+ hasItemSeparator,
663
+ numColumns,
664
+ otherAxisPos,
665
+ otherAxisSize
666
+ }) {
667
+ let paddingStyles;
668
+ if (columnWrapperStyle) {
669
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
670
+ if (horizontal) {
671
+ paddingStyles = {
672
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
673
+ paddingRight: columnGap || gap || void 0,
674
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
675
+ };
676
+ } else {
677
+ paddingStyles = {
678
+ paddingBottom: rowGap || gap || void 0,
679
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
680
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
681
+ };
682
+ }
683
+ }
684
+ return horizontal ? {
685
+ boxSizing: paddingStyles ? "border-box" : void 0,
686
+ flexDirection: hasItemSeparator ? "row" : void 0,
687
+ height: otherAxisSize,
688
+ left: 0,
689
+ position: "absolute",
690
+ top: otherAxisPos,
691
+ ...paddingStyles || {}
692
+ } : {
693
+ boxSizing: paddingStyles ? "border-box" : void 0,
694
+ left: otherAxisPos,
695
+ position: "absolute",
696
+ right: numColumns > 1 ? null : 0,
697
+ top: 0,
698
+ width: otherAxisSize,
699
+ ...paddingStyles || {}
700
+ };
701
+ }
649
702
  var Container = typedMemo(function Container2({
650
703
  id,
651
704
  recycleItems,
@@ -684,42 +737,17 @@ var Container = typedMemo(function Container2({
684
737
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
685
738
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
686
739
  const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
687
- const style = useMemo(() => {
688
- let paddingStyles;
689
- if (columnWrapperStyle) {
690
- const { columnGap, rowGap, gap } = columnWrapperStyle;
691
- if (horizontal) {
692
- paddingStyles = {
693
- paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
694
- paddingRight: columnGap || gap || void 0,
695
- paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
696
- };
697
- } else {
698
- paddingStyles = {
699
- paddingBottom: rowGap || gap || void 0,
700
- paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
701
- paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
702
- };
703
- }
704
- }
705
- return horizontal ? {
706
- boxSizing: paddingStyles ? "border-box" : void 0,
707
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
708
- height: otherAxisSize,
709
- left: 0,
710
- position: "absolute",
711
- top: otherAxisPos,
712
- ...paddingStyles || {}
713
- } : {
714
- boxSizing: paddingStyles ? "border-box" : void 0,
715
- left: otherAxisPos,
716
- position: "absolute",
717
- right: numColumns > 1 ? null : 0,
718
- top: 0,
719
- width: otherAxisSize,
720
- ...paddingStyles || {}
721
- };
722
- }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
740
+ const style = useMemo(
741
+ () => getContainerPositionStyle({
742
+ columnWrapperStyle,
743
+ hasItemSeparator: !!ItemSeparatorComponent,
744
+ horizontal,
745
+ numColumns,
746
+ otherAxisPos,
747
+ otherAxisSize
748
+ }),
749
+ [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
750
+ );
723
751
  const renderedItemInfo = useMemo(
724
752
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
725
753
  [itemKey, data, extraData]
@@ -2677,6 +2705,15 @@ function abortBootstrapInitialScroll(ctx) {
2677
2705
  }
2678
2706
 
2679
2707
  // src/core/initialScrollLifecycle.ts
2708
+ function retargetActiveInitialScrollAtEnd(ctx) {
2709
+ var _a3;
2710
+ const state = ctx.state;
2711
+ const initialScroll = state.initialScroll;
2712
+ if (!initialScroll || state.didFinishInitialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
2713
+ return false;
2714
+ }
2715
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
2716
+ }
2680
2717
  function handleInitialScrollLayoutReady(ctx) {
2681
2718
  var _a3;
2682
2719
  if (!ctx.state.initialScroll) {
@@ -4775,17 +4812,32 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4775
4812
  nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4776
4813
  }
4777
4814
  }
4778
- if (previousSize === nextSize) {
4779
- return nextSize;
4780
- }
4781
- set$(ctx, "anchoredEndSpaceSize", nextSize);
4782
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4783
- if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
4784
- updateScroll(ctx, state.scroll, true);
4815
+ if (previousSize !== nextSize) {
4816
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4817
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4818
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
4819
+ updateScroll(ctx, state.scroll, true);
4820
+ }
4785
4821
  }
4786
4822
  return nextSize;
4787
4823
  }
4788
4824
 
4825
+ // src/core/updateContentInsetEndAdjustment.ts
4826
+ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
4827
+ const state = ctx.state;
4828
+ const previousContentInsetEnd = getContentInsetEnd(ctx, previousContentInsetEndAdjustment);
4829
+ const nextContentInsetEnd = getContentInsetEnd(ctx);
4830
+ const insetDiff = nextContentInsetEnd - previousContentInsetEnd;
4831
+ if (insetDiff !== 0) {
4832
+ const wasWithinEndThreshold = !!peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4833
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4834
+ const didRetargetInitialScroll = retargetActiveInitialScrollAtEnd(ctx);
4835
+ if (!didRetargetInitialScroll && wasWithinEndThreshold && (Platform.OS !== "web" || insetDiff > 0)) {
4836
+ requestAdjust(ctx, insetDiff);
4837
+ }
4838
+ }
4839
+ }
4840
+
4789
4841
  // src/core/updateItemSize.ts
4790
4842
  function runOrScheduleMVCPRecalculate(ctx) {
4791
4843
  const state = ctx.state;
@@ -5025,14 +5077,14 @@ function createImperativeHandle(ctx) {
5025
5077
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5026
5078
  let imperativeScrollToken = 0;
5027
5079
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5028
- const runWhenSettled = (token, run) => {
5080
+ const runWhenReady = (token, run, isReady) => {
5029
5081
  const startedAt = Date.now();
5030
5082
  let stableFrames = 0;
5031
5083
  const check = () => {
5032
5084
  if (token !== imperativeScrollToken) {
5033
5085
  return;
5034
5086
  }
5035
- if (isSettlingAfterDataChange()) {
5087
+ if (isSettlingAfterDataChange() || !isReady()) {
5036
5088
  stableFrames = 0;
5037
5089
  } else {
5038
5090
  stableFrames += 1;
@@ -5047,10 +5099,10 @@ function createImperativeHandle(ctx) {
5047
5099
  requestAnimationFrame(check);
5048
5100
  };
5049
5101
  const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5050
- var _a3;
5102
+ var _a3, _b;
5051
5103
  const token = ++imperativeScrollToken;
5052
- const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
5053
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5104
+ const isReady = (_a3 = options == null ? void 0 : options.isReady) != null ? _a3 : (() => true);
5105
+ (_b = state.pendingScrollResolve) == null ? void 0 : _b.call(state);
5054
5106
  state.pendingScrollResolve = resolve;
5055
5107
  const runNow = () => {
5056
5108
  if (token !== imperativeScrollToken) {
@@ -5064,11 +5116,10 @@ function createImperativeHandle(ctx) {
5064
5116
  resolve();
5065
5117
  }
5066
5118
  };
5067
- const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
5068
- if (isSettlingAfterDataChange()) {
5069
- runWhenSettled(token, execute);
5119
+ if (isSettlingAfterDataChange() || !isReady()) {
5120
+ runWhenReady(token, runNow, isReady);
5070
5121
  } else {
5071
- execute();
5122
+ runNow();
5072
5123
  }
5073
5124
  });
5074
5125
  const scrollIndexIntoView = (options) => {
@@ -5149,8 +5200,14 @@ function createImperativeHandle(ctx) {
5149
5200
  startBuffered: state.startBuffered
5150
5201
  }),
5151
5202
  reportContentInset: (inset) => {
5203
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5204
+ const previousInset = state.contentInsetOverride;
5152
5205
  state.contentInsetOverride = inset != null ? inset : void 0;
5206
+ const didChange = ((_a3 = previousInset == null ? void 0 : previousInset.top) != null ? _a3 : 0) !== ((_c = (_b = state.contentInsetOverride) == null ? void 0 : _b.top) != null ? _c : 0) || ((_d = previousInset == null ? void 0 : previousInset.bottom) != null ? _d : 0) !== ((_f = (_e = state.contentInsetOverride) == null ? void 0 : _e.bottom) != null ? _f : 0) || ((_g = previousInset == null ? void 0 : previousInset.left) != null ? _g : 0) !== ((_i = (_h = state.contentInsetOverride) == null ? void 0 : _h.left) != null ? _i : 0) || ((_j = previousInset == null ? void 0 : previousInset.right) != null ? _j : 0) !== ((_l = (_k = state.contentInsetOverride) == null ? void 0 : _k.right) != null ? _l : 0);
5153
5207
  updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5208
+ if (didChange) {
5209
+ retargetActiveInitialScrollAtEnd(ctx);
5210
+ }
5154
5211
  },
5155
5212
  scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
5156
5213
  scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
@@ -5179,15 +5236,24 @@ function createImperativeHandle(ctx) {
5179
5236
  }
5180
5237
  return false;
5181
5238
  }),
5182
- scrollToIndex: (params) => runScrollWithPromise(
5183
- () => {
5239
+ scrollToIndex: (params) => {
5240
+ const shouldWaitForOutOfRangeTarget = params.index >= 0 && params.index >= state.props.data.length;
5241
+ const options = shouldWaitForOutOfRangeTarget ? {
5242
+ isReady: () => {
5243
+ var _a3;
5244
+ const props = state.props;
5245
+ const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5246
+ const lastIndex = props.data.length - 1;
5247
+ const isInRange = params.index < props.data.length;
5248
+ const shouldWaitForAnchorSize = isInRange && anchorIndex !== void 0 && anchorIndex >= 0 && params.index >= anchorIndex && !props.getFixedItemSize && !state.sizesKnown.has(getId(state, lastIndex));
5249
+ return isInRange && !shouldWaitForAnchorSize;
5250
+ }
5251
+ } : void 0;
5252
+ return runScrollWithPromise(() => {
5184
5253
  scrollToIndex(ctx, params);
5185
5254
  return true;
5186
- },
5187
- {
5188
- shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5189
- }
5190
- ),
5255
+ }, options);
5256
+ },
5191
5257
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5192
5258
  const data = state.props.data;
5193
5259
  const index = data.indexOf(item);
@@ -5442,6 +5508,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5442
5508
  data: dataProp = [],
5443
5509
  dataVersion,
5444
5510
  drawDistance = 250,
5511
+ contentInsetEndAdjustment,
5445
5512
  estimatedItemSize = 100,
5446
5513
  estimatedListSize,
5447
5514
  extraData,
@@ -5551,6 +5618,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5551
5618
  const combinedRef = useCombinedRef(refScroller, refScrollView);
5552
5619
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
5553
5620
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
5621
+ const contentInsetEndAdjustmentResolved = Platform.OS === "web" ? contentInsetEndAdjustment : void 0;
5622
+ const previousContentInsetEndAdjustmentRef = useRef(contentInsetEndAdjustmentResolved);
5554
5623
  const alwaysRenderIndices = useMemo(() => {
5555
5624
  const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
5556
5625
  return { arr: indices, set: new Set(indices) };
@@ -5670,6 +5739,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5670
5739
  anchoredEndSpace: anchoredEndSpaceResolved,
5671
5740
  animatedProps: animatedPropsInternal,
5672
5741
  contentInset,
5742
+ contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
5673
5743
  data: dataProp,
5674
5744
  dataVersion,
5675
5745
  drawDistance,
@@ -5792,6 +5862,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5792
5862
  didAnchoredEndSpaceAnchorIndexChange,
5793
5863
  numColumnsProp
5794
5864
  ]);
5865
+ useLayoutEffect(() => {
5866
+ const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
5867
+ previousContentInsetEndAdjustmentRef.current = contentInsetEndAdjustmentResolved;
5868
+ updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment);
5869
+ }, [ctx, contentInsetEndAdjustmentResolved]);
5795
5870
  const onLayoutFooter = useCallback(
5796
5871
  (layout) => {
5797
5872
  if (!usesBootstrapInitialScroll) {
@@ -5,7 +5,6 @@ type ScrollEventTarget = Window | HTMLElement;
5
5
 
6
6
  interface ScrollViewMethods {
7
7
  getBoundingClientRect(): DOMRect | null | undefined;
8
- getContentNode(): HTMLElement | null;
9
8
  getCurrentScrollOffset(): number;
10
9
  getScrollableNode(): HTMLElement;
11
10
  getScrollEventTarget(): ScrollEventTarget | null;
@@ -278,6 +277,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
278
277
  * Keeps an item visually anchored to the start by adding trailing space when the content below it underflows.
279
278
  */
280
279
  anchoredEndSpace?: AnchoredEndSpaceConfig$1;
280
+ /**
281
+ * Adjusts the effective end content inset for web lists without replacing the base contentInset.
282
+ * The adjustment is also rendered as real content padding so the browser scroll range includes it.
283
+ */
284
+ contentInsetEndAdjustment?: number;
281
285
  /**
282
286
  * Number of columns to render items in.
283
287
  * @default 1