@legendapp/list 3.0.0-beta.55 → 3.0.0-beta.56

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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React3 from 'react';
2
- import React3__default, { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useImperativeHandle, useLayoutEffect, useContext } from 'react';
2
+ import { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useImperativeHandle, useLayoutEffect, 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';
@@ -1215,10 +1215,16 @@ var StyleSheet = {
1215
1215
  create: (styles) => styles,
1216
1216
  flatten: (style) => flattenStyles(style)
1217
1217
  };
1218
+ function useLatestRef(value) {
1219
+ const ref = React3.useRef(value);
1220
+ ref.current = value;
1221
+ return ref;
1222
+ }
1223
+
1224
+ // src/utils/useRafCoalescer.ts
1218
1225
  function useRafCoalescer(callback) {
1219
- const callbackRef = useRef(callback);
1226
+ const callbackRef = useLatestRef(callback);
1220
1227
  const rafIdRef = useRef(void 0);
1221
- callbackRef.current = callback;
1222
1228
  const coalescer = useMemo(
1223
1229
  () => ({
1224
1230
  cancel() {
@@ -1780,6 +1786,19 @@ function WebAnchoredEndSpace({ horizontal }) {
1780
1786
  const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
1781
1787
  return /* @__PURE__ */ React3.createElement("div", { style }, null);
1782
1788
  }
1789
+ function useStableRenderComponent(renderComponent, mapProps) {
1790
+ const renderComponentRef = useLatestRef(renderComponent);
1791
+ const mapPropsRef = useLatestRef(mapProps);
1792
+ return React3.useMemo(
1793
+ () => React3.forwardRef(
1794
+ (props, ref) => {
1795
+ var _a3, _b;
1796
+ return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
1797
+ }
1798
+ ),
1799
+ [mapPropsRef, renderComponentRef]
1800
+ );
1801
+ }
1783
1802
  var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1784
1803
  const localRef = useRef(null);
1785
1804
  const ref = refView != null ? refView : localRef;
@@ -1824,14 +1843,11 @@ var ListComponent = typedMemo(function ListComponent2({
1824
1843
  needsOtherAxisSize: ctx.state.needsOtherAxisSize,
1825
1844
  otherAxisSize
1826
1845
  });
1827
- const ScrollComponent = useMemo(() => {
1828
- if (!renderScrollComponent) {
1829
- return ListComponentScrollView;
1830
- }
1831
- return React3.forwardRef(
1832
- (props, ref) => renderScrollComponent({ ...props, ref })
1833
- );
1834
- }, [renderScrollComponent]);
1846
+ const CustomScrollComponent = useStableRenderComponent(
1847
+ renderScrollComponent,
1848
+ (props, ref) => ({ ...props, ref })
1849
+ );
1850
+ const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
1835
1851
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1836
1852
  useLayoutEffect(() => {
1837
1853
  if (!ListHeaderComponent) {
@@ -3191,11 +3207,51 @@ function getObservedBootstrapInitialScrollOffset(state) {
3191
3207
  const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
3192
3208
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
3193
3209
  }
3210
+ function getPreservedEndAnchorOffsetDiff(ctx) {
3211
+ var _a3;
3212
+ const state = ctx.state;
3213
+ const initialScroll = state.initialScroll;
3214
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
3215
+ return;
3216
+ }
3217
+ const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
3218
+ return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
3219
+ }
3220
+ function schedulePreservedEndAnchorCorrection(ctx) {
3221
+ if (getPreservedEndAnchorOffsetDiff(ctx) === void 0) {
3222
+ return false;
3223
+ }
3224
+ const correction = {};
3225
+ schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3226
+ return true;
3227
+ }
3228
+ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
3229
+ const state = ctx.state;
3230
+ state.preservedEndAnchorCorrection = correction;
3231
+ requestAnimationFrame(() => {
3232
+ var _a3;
3233
+ const activeCorrection = state.preservedEndAnchorCorrection;
3234
+ if (activeCorrection !== correction) {
3235
+ return;
3236
+ }
3237
+ const offsetDiff = getPreservedEndAnchorOffsetDiff(ctx);
3238
+ if (offsetDiff === void 0 || Math.abs(offsetDiff) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3239
+ state.preservedEndAnchorCorrection = void 0;
3240
+ return;
3241
+ }
3242
+ const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
3243
+ if (hasObservedNativeScrollAfterRequest) {
3244
+ activeCorrection.lastRequestTime = Date.now();
3245
+ requestAdjust(ctx, offsetDiff);
3246
+ }
3247
+ schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3248
+ });
3249
+ }
3194
3250
  function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
3195
3251
  var _a3, _b;
3196
3252
  const state = ctx.state;
3197
3253
  const initialScroll = state.initialScroll;
3198
- if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
3254
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
3199
3255
  return;
3200
3256
  }
3201
3257
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
@@ -3352,7 +3408,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3352
3408
  }
3353
3409
  }
3354
3410
  function handleBootstrapInitialScrollLayoutChange(ctx) {
3355
- var _a3, _b, _c, _d;
3411
+ var _a3, _b, _c;
3356
3412
  const state = ctx.state;
3357
3413
  const initialScroll = state.initialScroll;
3358
3414
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
@@ -3363,7 +3419,9 @@ function handleBootstrapInitialScrollLayoutChange(ctx) {
3363
3419
  const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
3364
3420
  const offsetDiff = resolvedOffset - currentOffset;
3365
3421
  if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3366
- if (scrollingTo) {
3422
+ if (state.didFinishInitialScroll) {
3423
+ schedulePreservedEndAnchorCorrection(ctx);
3424
+ } else if (scrollingTo) {
3367
3425
  const existingWatchdog = initialScrollWatchdog.get(state);
3368
3426
  scrollingTo.offset = resolvedOffset;
3369
3427
  scrollingTo.targetOffset = resolvedOffset;
@@ -3376,14 +3434,8 @@ function handleBootstrapInitialScrollLayoutChange(ctx) {
3376
3434
  startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
3377
3435
  targetOffset: resolvedOffset
3378
3436
  });
3437
+ requestAdjust(ctx, offsetDiff);
3379
3438
  }
3380
- requestAdjust(ctx, offsetDiff);
3381
- if (state.didFinishInitialScroll) {
3382
- (_d = state.triggerCalculateItemsInView) == null ? void 0 : _d.call(state, { forceFullItemPositions: true });
3383
- }
3384
- }
3385
- if (state.didFinishInitialScroll) {
3386
- clearFinishedViewportRetargetableInitialScroll(state);
3387
3439
  }
3388
3440
  } else {
3389
3441
  rearmBootstrapInitialScroll(ctx, {
@@ -3634,7 +3686,10 @@ function retargetActiveInitialScrollAtEnd(ctx) {
3634
3686
  var _a3;
3635
3687
  const state = ctx.state;
3636
3688
  const initialScroll = state.initialScroll;
3637
- if (!initialScroll || state.didFinishInitialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3689
+ if (state.didFinishInitialScroll) {
3690
+ return schedulePreservedEndAnchorCorrection(ctx);
3691
+ }
3692
+ if (!initialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3638
3693
  return false;
3639
3694
  }
3640
3695
  return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
@@ -5637,7 +5692,7 @@ function cloneScrollEvent(event) {
5637
5692
  };
5638
5693
  }
5639
5694
  function onScroll(ctx, event) {
5640
- var _a3, _b, _c, _d, _e;
5695
+ var _a3, _b, _c, _d, _e, _f;
5641
5696
  const state = ctx.state;
5642
5697
  const { scrollProcessingEnabled } = state;
5643
5698
  if (scrollProcessingEnabled === false) {
@@ -5659,6 +5714,9 @@ function onScroll(ctx, event) {
5659
5714
  if (state.props.horizontal) {
5660
5715
  newScroll = toLogicalHorizontalOffset(state, newScroll, (_e = event.nativeEvent.contentSize) == null ? void 0 : _e.width);
5661
5716
  }
5717
+ state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.scroll > state.scrollLength;
5718
+ state.lastNativeScroll = newScroll;
5719
+ state.lastNativeScrollTime = Date.now();
5662
5720
  if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
5663
5721
  const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
5664
5722
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
@@ -6284,6 +6342,8 @@ function getAlwaysRenderIndices(config, data, keyExtractor, anchoredEndSpaceAnch
6284
6342
  indices.sort(sortAsc);
6285
6343
  return indices;
6286
6344
  }
6345
+
6346
+ // src/utils/getRenderedItem.ts
6287
6347
  function getRenderedItem(ctx, key) {
6288
6348
  var _a3;
6289
6349
  const state = ctx.state;
@@ -6309,7 +6369,7 @@ function getRenderedItem(ctx, key) {
6309
6369
  item,
6310
6370
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
6311
6371
  };
6312
- renderedItem = React3__default.createElement(renderItem, itemProps);
6372
+ renderedItem = renderItem(itemProps);
6313
6373
  }
6314
6374
  return { index, item: data[index], renderedItem };
6315
6375
  }
@@ -7035,6 +7095,8 @@ var internal = {
7035
7095
  typedMemo,
7036
7096
  useArr$,
7037
7097
  useCombinedRef,
7098
+ useLatestRef,
7099
+ useStableRenderComponent,
7038
7100
  useStateContext
7039
7101
  };
7040
7102
 
package/reanimated.d.ts CHANGED
@@ -92,13 +92,11 @@ interface DataModeProps<ItemT, TItemType extends string | undefined> {
92
92
  */
93
93
  data: ReadonlyArray<ItemT>;
94
94
  /**
95
- * Function or React component to render each item in the list.
96
- * Can be either:
97
- * - A function: (props: LegendListRenderItemProps<ItemT>) => ReactNode
98
- * - A React component: React.ComponentType<LegendListRenderItemProps<ItemT>>
95
+ * Callback to render each item in the list.
96
+ * To use hooks in an item component, return that component from this callback.
99
97
  * @required when using data mode
100
98
  */
101
- renderItem: ((props: LegendListRenderItemProps<ItemT, TItemType>) => React.ReactNode) | React.ComponentType<LegendListRenderItemProps<ItemT, TItemType>>;
99
+ renderItem: (props: LegendListRenderItemProps<ItemT, TItemType>) => React.ReactNode;
102
100
  children?: never;
103
101
  }
104
102
  interface ChildrenModeProps {
package/reanimated.js CHANGED
@@ -36,6 +36,8 @@ var {
36
36
  typedMemo,
37
37
  useArr$,
38
38
  useCombinedRef,
39
+ useLatestRef,
40
+ useStableRenderComponent,
39
41
  getComponent
40
42
  } = reactNative.internal;
41
43
  var { peek$, useStateContext } = reactNative.internal;
@@ -48,12 +50,12 @@ var ReanimatedScrollBridge = typedMemo(function ReanimatedScrollBridgeComponent(
48
50
  const animatedScrollRef = Reanimated.useAnimatedRef();
49
51
  Reanimated.useScrollViewOffset(animatedScrollRef, scrollOffset);
50
52
  const combinedRef = useCombinedRef(animatedScrollRef, forwardedRef);
51
- const ScrollComponent = React__namespace.useMemo(
52
- () => renderScrollComponent ? React__namespace.forwardRef(
53
- (scrollViewProps, ref) => renderScrollComponent({ ...scrollViewProps, ref, scrollEventThrottle: 1 })
54
- ) : Reanimated__default.default.ScrollView,
55
- [renderScrollComponent]
56
- );
53
+ const CustomScrollComponent = useStableRenderComponent(renderScrollComponent, (scrollViewProps, ref) => ({
54
+ ...scrollViewProps,
55
+ ref,
56
+ scrollEventThrottle: 1
57
+ }));
58
+ const ScrollComponent = renderScrollComponent ? CustomScrollComponent : Reanimated__default.default.ScrollView;
57
59
  return /* @__PURE__ */ React__namespace.createElement(ScrollComponent, { ...props, ref: combinedRef });
58
60
  });
59
61
  var StickyOverlay = typedMemo(function StickyOverlayComponent({ stickyHeaderConfig }) {
@@ -218,8 +220,7 @@ var LegendListForwardedRef = typedMemo(
218
220
  },
219
221
  [scrollOffset]
220
222
  );
221
- const itemLayoutAnimationRef = React__namespace.useRef(itemLayoutAnimation);
222
- itemLayoutAnimationRef.current = itemLayoutAnimation;
223
+ const itemLayoutAnimationRef = useLatestRef(itemLayoutAnimation);
223
224
  const hasItemLayoutAnimation = !!itemLayoutAnimation;
224
225
  const positionComponentInternal = React__namespace.useMemo(() => {
225
226
  if (!hasItemLayoutAnimation) {
package/reanimated.mjs CHANGED
@@ -12,6 +12,8 @@ var {
12
12
  typedMemo,
13
13
  useArr$,
14
14
  useCombinedRef,
15
+ useLatestRef,
16
+ useStableRenderComponent,
15
17
  getComponent
16
18
  } = internal;
17
19
  var { peek$, useStateContext } = internal;
@@ -24,12 +26,12 @@ var ReanimatedScrollBridge = typedMemo(function ReanimatedScrollBridgeComponent(
24
26
  const animatedScrollRef = useAnimatedRef();
25
27
  useScrollViewOffset(animatedScrollRef, scrollOffset);
26
28
  const combinedRef = useCombinedRef(animatedScrollRef, forwardedRef);
27
- const ScrollComponent = React.useMemo(
28
- () => renderScrollComponent ? React.forwardRef(
29
- (scrollViewProps, ref) => renderScrollComponent({ ...scrollViewProps, ref, scrollEventThrottle: 1 })
30
- ) : Reanimated.ScrollView,
31
- [renderScrollComponent]
32
- );
29
+ const CustomScrollComponent = useStableRenderComponent(renderScrollComponent, (scrollViewProps, ref) => ({
30
+ ...scrollViewProps,
31
+ ref,
32
+ scrollEventThrottle: 1
33
+ }));
34
+ const ScrollComponent = renderScrollComponent ? CustomScrollComponent : Reanimated.ScrollView;
33
35
  return /* @__PURE__ */ React.createElement(ScrollComponent, { ...props, ref: combinedRef });
34
36
  });
35
37
  var StickyOverlay = typedMemo(function StickyOverlayComponent({ stickyHeaderConfig }) {
@@ -194,8 +196,7 @@ var LegendListForwardedRef = typedMemo(
194
196
  },
195
197
  [scrollOffset]
196
198
  );
197
- const itemLayoutAnimationRef = React.useRef(itemLayoutAnimation);
198
- itemLayoutAnimationRef.current = itemLayoutAnimation;
199
+ const itemLayoutAnimationRef = useLatestRef(itemLayoutAnimation);
199
200
  const hasItemLayoutAnimation = !!itemLayoutAnimation;
200
201
  const positionComponentInternal = React.useMemo(() => {
201
202
  if (!hasItemLayoutAnimation) {
package/section-list.d.ts CHANGED
@@ -148,13 +148,11 @@ interface DataModeProps<ItemT, TItemType extends string | undefined> {
148
148
  */
149
149
  data: ReadonlyArray<ItemT>;
150
150
  /**
151
- * Function or React component to render each item in the list.
152
- * Can be either:
153
- * - A function: (props: LegendListRenderItemProps<ItemT>) => ReactNode
154
- * - A React component: React.ComponentType<LegendListRenderItemProps<ItemT>>
151
+ * Callback to render each item in the list.
152
+ * To use hooks in an item component, return that component from this callback.
155
153
  * @required when using data mode
156
154
  */
157
- renderItem: ((props: LegendListRenderItemProps<ItemT, TItemType>) => React.ReactNode) | React.ComponentType<LegendListRenderItemProps<ItemT, TItemType>>;
155
+ renderItem: (props: LegendListRenderItemProps<ItemT, TItemType>) => React.ReactNode;
158
156
  children?: never;
159
157
  }
160
158
  interface ChildrenModeProps {