@legendapp/list 3.0.0-beta.52 → 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/animated.d.ts CHANGED
@@ -248,6 +248,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
248
248
  * Keeps an item visually anchored to the start by adding trailing space when the content below it underflows.
249
249
  */
250
250
  anchoredEndSpace?: AnchoredEndSpaceConfig;
251
+ /**
252
+ * Adjusts the effective end content inset for web lists without replacing the base contentInset.
253
+ * The adjustment is also rendered as real content padding so the browser scroll range includes it.
254
+ */
255
+ contentInsetEndAdjustment?: number;
251
256
  /**
252
257
  * Number of columns to render items in.
253
258
  * @default 1
@@ -641,7 +646,7 @@ interface ViewabilityConfig {
641
646
  waitForInteraction?: boolean | undefined;
642
647
  }
643
648
 
644
- type LegendListPropsOverrides<ItemT, TItemType extends string | undefined> = Omit<LegendListPropsBase<ItemT, ScrollViewProps, TItemType>, "anchoredEndSpace" | "onScroll" | "refScrollView" | "renderScrollComponent" | "ListHeaderComponentStyle" | "ListFooterComponentStyle"> & {
649
+ type LegendListPropsOverrides<ItemT, TItemType extends string | undefined> = Omit<LegendListPropsBase<ItemT, ScrollViewProps, TItemType>, "anchoredEndSpace" | "contentInsetEndAdjustment" | "onScroll" | "refScrollView" | "renderScrollComponent" | "ListHeaderComponentStyle" | "ListFooterComponentStyle"> & {
645
650
  onScroll?: (event: NativeSyntheticEvent$1<NativeScrollEvent$1>) => void;
646
651
  refScrollView?: React.Ref<ScrollView>;
647
652
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
package/index.d.ts CHANGED
@@ -249,6 +249,7 @@ interface InternalState$1 {
249
249
  data: readonly any[];
250
250
  dataVersion: Key | undefined;
251
251
  drawDistance: number;
252
+ contentInsetEndAdjustment: number | undefined;
252
253
  estimatedItemSize: number | undefined;
253
254
  getEstimatedItemSize: LegendListPropsInternal["getEstimatedItemSize"];
254
255
  getFixedItemSize: LegendListPropsInternal["getFixedItemSize"];
@@ -573,6 +574,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
573
574
  * Keeps an item visually anchored to the start by adding trailing space when the content below it underflows.
574
575
  */
575
576
  anchoredEndSpace?: AnchoredEndSpaceConfig$1;
577
+ /**
578
+ * Adjusts the effective end content inset for web lists without replacing the base contentInset.
579
+ * The adjustment is also rendered as real content padding so the browser scroll range includes it.
580
+ */
581
+ contentInsetEndAdjustment?: number;
576
582
  /**
577
583
  * Number of columns to render items in.
578
584
  * @default 1
package/index.js CHANGED
@@ -166,7 +166,10 @@ function useSelector$(signalName, selector) {
166
166
  }
167
167
 
168
168
  // src/state/getContentInsetEnd.ts
169
- function getContentInsetEnd(ctx) {
169
+ function getContentInsetEndAdjustmentEnd(adjustment) {
170
+ return Math.max(0, adjustment != null ? adjustment : 0);
171
+ }
172
+ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
170
173
  var _a3, _b;
171
174
  const state = ctx.state;
172
175
  const { props } = state;
@@ -174,14 +177,21 @@ function getContentInsetEnd(ctx) {
174
177
  const contentInset = props.contentInset;
175
178
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
176
179
  const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
180
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
181
+ contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
182
+ );
177
183
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
178
184
  const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
179
185
  const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
186
+ const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
180
187
  if (overrideInset) {
181
188
  const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
182
- return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
189
+ return Math.max(
190
+ ((horizontal ? mergedInset.right : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
191
+ anchoredEndInset
192
+ );
183
193
  }
184
- return Math.max(baseEndInset, anchoredEndInset);
194
+ return Math.max(adjustedBaseEndInset, anchoredEndInset);
185
195
  }
186
196
 
187
197
  // src/state/getContentSize.ts
@@ -679,6 +689,49 @@ function isInMVCPActiveMode(state) {
679
689
  }
680
690
 
681
691
  // src/components/Container.tsx
692
+ function getContainerPositionStyle({
693
+ columnWrapperStyle,
694
+ horizontal,
695
+ hasItemSeparator,
696
+ numColumns,
697
+ otherAxisPos,
698
+ otherAxisSize
699
+ }) {
700
+ let paddingStyles;
701
+ if (columnWrapperStyle) {
702
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
703
+ if (horizontal) {
704
+ paddingStyles = {
705
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
706
+ paddingRight: columnGap || gap || void 0,
707
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
708
+ };
709
+ } else {
710
+ paddingStyles = {
711
+ paddingBottom: rowGap || gap || void 0,
712
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
713
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
714
+ };
715
+ }
716
+ }
717
+ return horizontal ? {
718
+ boxSizing: paddingStyles ? "border-box" : void 0,
719
+ flexDirection: hasItemSeparator ? "row" : void 0,
720
+ height: otherAxisSize,
721
+ left: 0,
722
+ position: "absolute",
723
+ top: otherAxisPos,
724
+ ...paddingStyles || {}
725
+ } : {
726
+ boxSizing: paddingStyles ? "border-box" : void 0,
727
+ left: otherAxisPos,
728
+ position: "absolute",
729
+ right: numColumns > 1 ? null : 0,
730
+ top: 0,
731
+ width: otherAxisSize,
732
+ ...paddingStyles || {}
733
+ };
734
+ }
682
735
  var Container = typedMemo(function Container2({
683
736
  id,
684
737
  recycleItems,
@@ -717,42 +770,17 @@ var Container = typedMemo(function Container2({
717
770
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
718
771
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
719
772
  const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
720
- const style = React3.useMemo(() => {
721
- let paddingStyles;
722
- if (columnWrapperStyle) {
723
- const { columnGap, rowGap, gap } = columnWrapperStyle;
724
- if (horizontal) {
725
- paddingStyles = {
726
- paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
727
- paddingRight: columnGap || gap || void 0,
728
- paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
729
- };
730
- } else {
731
- paddingStyles = {
732
- paddingBottom: rowGap || gap || void 0,
733
- paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
734
- paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
735
- };
736
- }
737
- }
738
- return horizontal ? {
739
- boxSizing: paddingStyles ? "border-box" : void 0,
740
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
741
- height: otherAxisSize,
742
- left: 0,
743
- position: "absolute",
744
- top: otherAxisPos,
745
- ...paddingStyles || {}
746
- } : {
747
- boxSizing: paddingStyles ? "border-box" : void 0,
748
- left: otherAxisPos,
749
- position: "absolute",
750
- right: numColumns > 1 ? null : 0,
751
- top: 0,
752
- width: otherAxisSize,
753
- ...paddingStyles || {}
754
- };
755
- }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
773
+ const style = React3.useMemo(
774
+ () => getContainerPositionStyle({
775
+ columnWrapperStyle,
776
+ hasItemSeparator: !!ItemSeparatorComponent,
777
+ horizontal,
778
+ numColumns,
779
+ otherAxisPos,
780
+ otherAxisSize
781
+ }),
782
+ [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
783
+ );
756
784
  const renderedItemInfo = React3.useMemo(
757
785
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
758
786
  [itemKey, data, extraData]
@@ -1063,6 +1091,9 @@ function useRafCoalescer(callback) {
1063
1091
  return coalescer;
1064
1092
  }
1065
1093
 
1094
+ // src/components/webConstants.ts
1095
+ var LEGEND_LIST_CONTENT_CONTAINER_CLASS = "legend-list-content-container";
1096
+
1066
1097
  // src/components/webScrollUtils.ts
1067
1098
  function getDocumentScrollerNode() {
1068
1099
  if (typeof document === "undefined") {
@@ -1147,6 +1178,11 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1147
1178
  }
1148
1179
 
1149
1180
  // src/components/ListComponentScrollView.tsx
1181
+ function getContentInsetEndAdjustmentEnd2(ctx) {
1182
+ var _a3, _b;
1183
+ const adjustment = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.props) == null ? void 0 : _b.contentInsetEndAdjustment;
1184
+ return Math.max(0, adjustment != null ? adjustment : 0);
1185
+ }
1150
1186
  var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView2({
1151
1187
  children,
1152
1188
  style,
@@ -1164,7 +1200,9 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1164
1200
  onLayout,
1165
1201
  ...props
1166
1202
  }, ref) {
1203
+ var _a3, _b, _c;
1167
1204
  const ctx = useStateContext();
1205
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1168
1206
  const scrollRef = React3.useRef(null);
1169
1207
  const contentRef = React3.useRef(null);
1170
1208
  const isWindowScroll = useWindowScroll;
@@ -1226,10 +1264,9 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1226
1264
  React3.useImperativeHandle(ref, () => {
1227
1265
  const api = {
1228
1266
  getBoundingClientRect: () => {
1229
- var _a3;
1230
- return (_a3 = scrollRef.current) == null ? void 0 : _a3.getBoundingClientRect();
1267
+ var _a4;
1268
+ return (_a4 = scrollRef.current) == null ? void 0 : _a4.getBoundingClientRect();
1231
1269
  },
1232
- getContentNode: () => contentRef.current,
1233
1270
  getCurrentScrollOffset,
1234
1271
  getScrollableNode: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1235
1272
  getScrollEventTarget: () => getScrollTarget(),
@@ -1355,6 +1392,10 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1355
1392
  },
1356
1393
  ...StyleSheet.flatten(style)
1357
1394
  };
1395
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd2(ctx);
1396
+ 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;
1397
+ const renderedContentInsetEndAdjustment = Math.max(0, contentInsetEndAdjustment - anchoredEndInset);
1398
+ const contentInsetEndAdjustmentSpacerStyle = renderedContentInsetEndAdjustment ? horizontal ? { flexShrink: 0, width: renderedContentInsetEndAdjustment } : { height: renderedContentInsetEndAdjustment } : void 0;
1358
1399
  const contentStyle = {
1359
1400
  display: horizontal ? "flex" : "block",
1360
1401
  flexDirection: horizontal ? "row" : void 0,
@@ -1362,6 +1403,7 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1362
1403
  minWidth: horizontal ? "100%" : void 0,
1363
1404
  ...StyleSheet.flatten(contentContainerStyle)
1364
1405
  };
1406
+ const className = contentContainerClassName ? `${LEGEND_LIST_CONTENT_CONTAINER_CLASS} ${contentContainerClassName}` : LEGEND_LIST_CONTENT_CONTAINER_CLASS;
1365
1407
  const {
1366
1408
  contentContainerClassName: _contentContainerClassName,
1367
1409
  contentInset: _contentInset,
@@ -1370,7 +1412,7 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1370
1412
  useWindowScroll: _useWindowScroll,
1371
1413
  ...webProps
1372
1414
  } = props;
1373
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1415
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { className, ref: contentRef, style: contentStyle }, children, contentInsetEndAdjustmentSpacerStyle ? /* @__PURE__ */ React3__namespace.createElement("div", { "aria-hidden": true, style: contentInsetEndAdjustmentSpacerStyle }) : null));
1374
1416
  });
1375
1417
  function useValueListener$(key, callback) {
1376
1418
  const ctx = useStateContext();
@@ -1398,11 +1440,18 @@ function getScrollAdjustAxis(horizontal) {
1398
1440
  y: 1
1399
1441
  };
1400
1442
  }
1443
+ function resolveScrollAdjustContentNode(el, contentNode) {
1444
+ if ((contentNode == null ? void 0 : contentNode.isConnected) && contentNode.parentElement === el) {
1445
+ return contentNode;
1446
+ }
1447
+ return el.querySelector(`:scope > .${LEGEND_LIST_CONTENT_CONTAINER_CLASS}`);
1448
+ }
1401
1449
  function ScrollAdjust() {
1402
1450
  const ctx = useStateContext();
1403
1451
  const lastScrollOffsetRef = React3__namespace.useRef(0);
1404
1452
  const resetPaddingRafRef = React3__namespace.useRef(void 0);
1405
1453
  const resetPaddingBaselineRef = React3__namespace.useRef(void 0);
1454
+ const contentNodeRef = React3__namespace.useRef(null);
1406
1455
  const callback = React3__namespace.useCallback(() => {
1407
1456
  var _a3, _b;
1408
1457
  const scrollAdjust = peek$(ctx, "scrollAdjust");
@@ -1413,9 +1462,10 @@ function ScrollAdjust() {
1413
1462
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1414
1463
  if (scrollDelta !== 0) {
1415
1464
  const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1416
- const contentNode = scrollView.getContentNode();
1417
1465
  const prevScroll = scrollView.getCurrentScrollOffset();
1418
1466
  const el = scrollView.getScrollableNode();
1467
+ const contentNode = resolveScrollAdjustContentNode(el, contentNodeRef.current);
1468
+ contentNodeRef.current = contentNode;
1419
1469
  const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1420
1470
  if (!contentNode) {
1421
1471
  scrollBy();
@@ -3270,6 +3320,15 @@ function checkFinishedScrollFallback(ctx) {
3270
3320
  }
3271
3321
 
3272
3322
  // src/core/initialScrollLifecycle.ts
3323
+ function retargetActiveInitialScrollAtEnd(ctx) {
3324
+ var _a3;
3325
+ const state = ctx.state;
3326
+ const initialScroll = state.initialScroll;
3327
+ if (!initialScroll || state.didFinishInitialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3328
+ return false;
3329
+ }
3330
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3331
+ }
3273
3332
  function handleInitialScrollLayoutReady(ctx) {
3274
3333
  var _a3;
3275
3334
  if (!ctx.state.initialScroll) {
@@ -5336,6 +5395,22 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5336
5395
  return nextSize;
5337
5396
  }
5338
5397
 
5398
+ // src/core/updateContentInsetEndAdjustment.ts
5399
+ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
5400
+ const state = ctx.state;
5401
+ const previousContentInsetEnd = getContentInsetEnd(ctx, previousContentInsetEndAdjustment);
5402
+ const nextContentInsetEnd = getContentInsetEnd(ctx);
5403
+ const insetDiff = nextContentInsetEnd - previousContentInsetEnd;
5404
+ if (insetDiff !== 0) {
5405
+ const wasWithinEndThreshold = !!peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
5406
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5407
+ const didRetargetInitialScroll = retargetActiveInitialScrollAtEnd(ctx);
5408
+ if (!didRetargetInitialScroll && wasWithinEndThreshold && (insetDiff > 0)) {
5409
+ requestAdjust(ctx, insetDiff);
5410
+ }
5411
+ }
5412
+ }
5413
+
5339
5414
  // src/core/updateItemSize.ts
5340
5415
  function runOrScheduleMVCPRecalculate(ctx) {
5341
5416
  const state = ctx.state;
@@ -5683,8 +5758,14 @@ function createImperativeHandle(ctx) {
5683
5758
  startBuffered: state.startBuffered
5684
5759
  }),
5685
5760
  reportContentInset: (inset) => {
5761
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5762
+ const previousInset = state.contentInsetOverride;
5686
5763
  state.contentInsetOverride = inset != null ? inset : void 0;
5764
+ 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);
5687
5765
  updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5766
+ if (didChange) {
5767
+ retargetActiveInitialScrollAtEnd(ctx);
5768
+ }
5688
5769
  },
5689
5770
  scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
5690
5771
  scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
@@ -5985,6 +6066,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5985
6066
  data: dataProp = [],
5986
6067
  dataVersion,
5987
6068
  drawDistance = 250,
6069
+ contentInsetEndAdjustment,
5988
6070
  estimatedItemSize = 100,
5989
6071
  estimatedListSize,
5990
6072
  extraData,
@@ -6094,6 +6176,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6094
6176
  const combinedRef = useCombinedRef(refScroller, refScrollView);
6095
6177
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
6096
6178
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
6179
+ const contentInsetEndAdjustmentResolved = contentInsetEndAdjustment ;
6180
+ const previousContentInsetEndAdjustmentRef = React3.useRef(contentInsetEndAdjustmentResolved);
6097
6181
  const alwaysRenderIndices = React3.useMemo(() => {
6098
6182
  const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
6099
6183
  return { arr: indices, set: new Set(indices) };
@@ -6213,6 +6297,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6213
6297
  anchoredEndSpace: anchoredEndSpaceResolved,
6214
6298
  animatedProps: animatedPropsInternal,
6215
6299
  contentInset,
6300
+ contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6216
6301
  data: dataProp,
6217
6302
  dataVersion,
6218
6303
  drawDistance,
@@ -6330,6 +6415,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6330
6415
  didAnchoredEndSpaceAnchorIndexChange,
6331
6416
  numColumnsProp
6332
6417
  ]);
6418
+ React3.useLayoutEffect(() => {
6419
+ const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
6420
+ previousContentInsetEndAdjustmentRef.current = contentInsetEndAdjustmentResolved;
6421
+ updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment);
6422
+ }, [ctx, contentInsetEndAdjustmentResolved]);
6333
6423
  const onLayoutFooter = React3.useCallback(
6334
6424
  (layout) => {
6335
6425
  if (!usesBootstrapInitialScroll) {