@legendapp/list 3.0.0-beta.44 → 3.0.0-beta.45

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/reanimated.d.ts CHANGED
@@ -1,18 +1,30 @@
1
1
  import * as React from 'react';
2
2
  import { Key, ComponentProps } from 'react';
3
- import Reanimated from 'react-native-reanimated';
3
+ import Reanimated, { SharedValue } from 'react-native-reanimated';
4
4
  import { ScrollViewProps, NativeSyntheticEvent as NativeSyntheticEvent$1, NativeScrollEvent as NativeScrollEvent$1, ScrollView, StyleProp as StyleProp$1, ViewStyle as ViewStyle$1, ScrollViewComponent, ScrollResponderMixin, Insets as Insets$1 } from 'react-native';
5
5
 
6
- type ListenerType = "activeStickyIndex" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
7
- type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
6
+ interface MaintainVisibleContentPositionNormalized<ItemT = any> {
7
+ data: boolean;
8
+ size: boolean;
9
+ shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
10
+ }
11
+
12
+ type ListenerType = "activeStickyIndex" | "anchoredEndSpaceSize" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemKey${number}` | `containerPosition${number}` | `containerSticky${number}`;
13
+ type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
8
14
  type ListenerTypeValueMap = {
9
15
  activeStickyIndex: number;
16
+ anchoredEndSpaceSize: number;
10
17
  animatedScrollY: any;
11
18
  debugComputedScroll: number;
12
19
  debugRawScroll: number;
13
20
  extraData: any;
14
21
  footerSize: number;
15
22
  headerSize: number;
23
+ isAtEnd: boolean;
24
+ isAtStart: boolean;
25
+ isNearEnd: boolean;
26
+ isNearStart: boolean;
27
+ isWithinMaintainScrollAtEndThreshold: boolean;
16
28
  lastItemKeys: string[];
17
29
  lastPositionUpdate: number;
18
30
  maintainVisibleContentPosition: MaintainVisibleContentPositionNormalized;
@@ -114,6 +126,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
114
126
  * Style applied to each column's wrapper view.
115
127
  */
116
128
  columnWrapperStyle?: ColumnWrapperStyle;
129
+ /**
130
+ * Version token that forces the list to treat data as updated even when the array reference is stable.
131
+ * Increment or change this when mutating the data array in place.
132
+ */
133
+ dataVersion?: Key;
117
134
  /**
118
135
  * Distance in pixels to pre-render items ahead of the visible area.
119
136
  * @default 250
@@ -137,33 +154,35 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
137
154
  * Extra data to trigger re-rendering when changed.
138
155
  */
139
156
  extraData?: any;
140
- /**
141
- * Version token that forces the list to treat data as updated even when the array reference is stable.
142
- * Increment or change this when mutating the data array in place.
143
- */
144
- dataVersion?: Key;
145
157
  /**
146
158
  * In case you have distinct item sizes, you can provide a function to get the size of an item.
147
- * Use instead of FlatList's getItemLayout or FlashList overrideItemLayout if you want to have accurate initialScrollOffset, you should provide this function
148
159
  */
149
160
  getEstimatedItemSize?: (item: ItemT, index: number, type: TItemType) => number;
150
161
  /**
151
- * Customize layout for multi-column lists, such as allowing items to span multiple columns.
152
- * Similar to FlashList's overrideItemLayout.
162
+ * In case items always have a fixed size, you can provide a function to return it.
153
163
  */
154
- overrideItemLayout?: (layout: {
155
- span?: number;
156
- }, item: ItemT, index: number, maxColumns: number, extraData?: any) => void;
164
+ getFixedItemSize?: (item: ItemT, index: number, type: TItemType) => number | undefined;
165
+ /**
166
+ * Returns a stable item type used for pooling and size estimation.
167
+ */
168
+ getItemType?: (item: ItemT, index: number) => TItemType;
169
+ /**
170
+ * Component to render between items, receiving the leading item as prop.
171
+ */
172
+ ItemSeparatorComponent?: React.ComponentType<{
173
+ leadingItem: ItemT;
174
+ }>;
157
175
  /**
158
176
  * Ratio of initial container pool size to data length (e.g., 0.5 for half).
159
177
  * @default 2
160
178
  */
161
179
  initialContainerPoolRatio?: number | undefined;
162
180
  /**
163
- * Initial scroll position in pixels.
164
- * @default 0
181
+ * When true, the list initializes scrolled to the last item.
182
+ * Overrides `initialScrollIndex` and `initialScrollOffset` when data is available.
183
+ * @default false
165
184
  */
166
- initialScrollOffset?: number;
185
+ initialScrollAtEnd?: boolean;
167
186
  /**
168
187
  * Index to scroll to initially.
169
188
  * @default 0
@@ -174,17 +193,14 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
174
193
  viewPosition?: number | undefined;
175
194
  };
176
195
  /**
177
- * When true, the list initializes scrolled to the last item.
178
- * Overrides `initialScrollIndex` and `initialScrollOffset` when data is available.
179
- * @default false
196
+ * Initial scroll position in pixels.
197
+ * @default 0
180
198
  */
181
- initialScrollAtEnd?: boolean;
199
+ initialScrollOffset?: number;
182
200
  /**
183
- * Component to render between items, receiving the leading item as prop.
201
+ * Custom equality function to detect semantically unchanged items.
184
202
  */
185
- ItemSeparatorComponent?: React.ComponentType<{
186
- leadingItem: ItemT;
187
- }>;
203
+ itemsAreEqual?: (itemPrevious: ItemT, item: ItemT, index: number, data: readonly ItemT[]) => boolean;
188
204
  /**
189
205
  * Function to extract a unique key for each item.
190
206
  */
@@ -230,10 +246,9 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
230
246
  */
231
247
  maintainVisibleContentPosition?: boolean | MaintainVisibleContentPositionConfig<ItemT>;
232
248
  /**
233
- * Web only: when true, listens to window/body scrolling instead of rendering a scrollable list container.
234
- * @default false
249
+ * Keeps an item visually anchored to the start by adding trailing space when the content below it underflows.
235
250
  */
236
- useWindowScroll?: boolean;
251
+ anchoredEndSpace?: AnchoredEndSpaceConfig;
237
252
  /**
238
253
  * Number of columns to render items in.
239
254
  * @default 1
@@ -260,6 +275,12 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
260
275
  itemKey: string;
261
276
  itemData: ItemT;
262
277
  }) => void;
278
+ /**
279
+ * Called after the initial render work completes.
280
+ */
281
+ onLoad?: (info: {
282
+ elapsedTimeInMs: number;
283
+ }) => void;
263
284
  /**
264
285
  * Called when list layout metrics change.
265
286
  */
@@ -268,6 +289,9 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
268
289
  * Function to call when the user pulls to refresh.
269
290
  */
270
291
  onRefresh?: () => void;
292
+ /**
293
+ * Called when the list scrolls.
294
+ */
271
295
  onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
272
296
  /**
273
297
  * Called when scrolling reaches the start within onStartReachedThreshold.
@@ -291,6 +315,12 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
291
315
  * Called when the viewability of items changes.
292
316
  */
293
317
  onViewableItemsChanged?: OnViewableItemsChanged<ItemT> | undefined;
318
+ /**
319
+ * Customize layout for multi-column lists, such as allowing items to span multiple columns.
320
+ */
321
+ overrideItemLayout?: (layout: {
322
+ span?: number;
323
+ }, item: ItemT, index: number, maxColumns: number, extraData?: any) => void;
294
324
  /**
295
325
  * Offset in pixels for the refresh indicator.
296
326
  * @default 0
@@ -316,6 +346,10 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
316
346
  * @default (props) => <ScrollView {...props} />
317
347
  */
318
348
  renderScrollComponent?: (props: any) => React.ReactElement | null;
349
+ /**
350
+ * Array of item indices to use as snap points.
351
+ */
352
+ snapToIndices?: number[];
319
353
  /**
320
354
  * This will log a suggested estimatedItemSize.
321
355
  * @required
@@ -330,10 +364,6 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
330
364
  * Pairs of viewability configs and their callbacks for tracking visibility.
331
365
  */
332
366
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs<ItemT> | undefined;
333
- onLoad?: (info: {
334
- elapsedTimeInMs: number;
335
- }) => void;
336
- snapToIndices?: number[];
337
367
  /**
338
368
  * Array of child indices determining which children get docked to the top of the screen when scrolling.
339
369
  * For example, passing stickyHeaderIndices={[0]} will cause the first child to be fixed to the top of the scroll view.
@@ -350,9 +380,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
350
380
  * @default undefined
351
381
  */
352
382
  stickyHeaderConfig?: StickyHeaderConfig;
353
- getItemType?: (item: ItemT, index: number) => TItemType;
354
- getFixedItemSize?: (item: ItemT, index: number, type: TItemType) => number | undefined;
355
- itemsAreEqual?: (itemPrevious: ItemT, item: ItemT, index: number, data: readonly ItemT[]) => boolean;
383
+ /**
384
+ * Web only: when true, listens to window/body scrolling instead of rendering a scrollable list container.
385
+ * @default false
386
+ */
387
+ useWindowScroll?: boolean;
356
388
  }
357
389
  type LegendListPropsBase<ItemT, TScrollViewProps = Record<string, any>, TItemType extends string | undefined = string | undefined> = BaseScrollViewProps<TScrollViewProps> & LegendListSpecificProps<ItemT, TItemType> & (DataModeProps<ItemT, TItemType> | ChildrenModeProps);
358
390
  interface MaintainVisibleContentPositionConfig<ItemT = any> {
@@ -360,10 +392,12 @@ interface MaintainVisibleContentPositionConfig<ItemT = any> {
360
392
  size?: boolean;
361
393
  shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
362
394
  }
363
- interface MaintainVisibleContentPositionNormalized<ItemT = any> {
364
- data: boolean;
365
- size: boolean;
366
- shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
395
+ interface AnchoredEndSpaceConfig {
396
+ anchorIndex: number;
397
+ anchorOffset?: number;
398
+ anchorMaxSize?: number;
399
+ includeInEndInset?: boolean;
400
+ onSizeChanged?: (size: number) => void;
367
401
  }
368
402
  interface StickyHeaderConfig {
369
403
  /**
@@ -426,8 +460,11 @@ type LegendListState = {
426
460
  endBuffered: number;
427
461
  isAtEnd: boolean;
428
462
  isAtStart: boolean;
463
+ isNearEnd: boolean;
464
+ isNearStart: boolean;
429
465
  isEndReached: boolean;
430
466
  isStartReached: boolean;
467
+ isWithinMaintainScrollAtEndThreshold: boolean;
431
468
  listen: <T extends LegendListListenerType>(listenerType: T, callback: (value: ListenerTypeValueMap[T]) => void) => () => void;
432
469
  listenToPosition: (key: string, callback: (value: number) => void) => () => void;
433
470
  positionAtIndex: (index: number) => number;
@@ -566,10 +603,15 @@ interface ViewabilityConfigCallbackPair<ItemT = any> {
566
603
  viewabilityConfig: ViewabilityConfig;
567
604
  }
568
605
  type ViewabilityConfigCallbackPairs<ItemT> = ViewabilityConfigCallbackPair<ItemT>[];
569
- type OnViewableItemsChanged<ItemT> = ((info: {
570
- viewableItems: Array<ViewToken<ItemT>>;
606
+ interface OnViewableItemsChangedInfo<ItemT> {
571
607
  changed: Array<ViewToken<ItemT>>;
572
- }) => void) | null;
608
+ end: number;
609
+ endBuffered: number;
610
+ start: number;
611
+ startBuffered: number;
612
+ viewableItems: Array<ViewToken<ItemT>>;
613
+ }
614
+ type OnViewableItemsChanged<ItemT> = ((info: OnViewableItemsChangedInfo<ItemT>) => void) | null;
573
615
  interface ViewabilityConfig {
574
616
  /**
575
617
  * A unique ID to identify this viewability config
@@ -600,7 +642,7 @@ interface ViewabilityConfig {
600
642
  waitForInteraction?: boolean | undefined;
601
643
  }
602
644
 
603
- type LegendListPropsOverrides<ItemT, TItemType extends string | undefined> = Omit<LegendListPropsBase<ItemT, ScrollViewProps, TItemType>, "onScroll" | "refScrollView" | "renderScrollComponent" | "ListHeaderComponentStyle" | "ListFooterComponentStyle"> & {
645
+ type LegendListPropsOverrides<ItemT, TItemType extends string | undefined> = Omit<LegendListPropsBase<ItemT, ScrollViewProps, TItemType>, "anchoredEndSpace" | "onScroll" | "refScrollView" | "renderScrollComponent" | "ListHeaderComponentStyle" | "ListFooterComponentStyle"> & {
604
646
  onScroll?: (event: NativeSyntheticEvent$1<NativeScrollEvent$1>) => void;
605
647
  refScrollView?: React.Ref<ScrollView>;
606
648
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
@@ -618,9 +660,19 @@ type KeysToOmit = "getEstimatedItemSize" | "getFixedItemSize" | "getItemType" |
618
660
  type PropsBase<ItemT> = LegendListProps<ItemT>;
619
661
  type AnimatedScrollView = React.ElementRef<typeof Reanimated.ScrollView>;
620
662
  type ReanimatedLayoutAnimation = ComponentProps<typeof Reanimated.View>["layout"];
663
+ interface AnimatedLegendListSharedValues {
664
+ activeStickyIndex?: SharedValue<number>;
665
+ isAtEnd?: SharedValue<boolean>;
666
+ isAtStart?: SharedValue<boolean>;
667
+ isNearEnd?: SharedValue<boolean>;
668
+ isNearStart?: SharedValue<boolean>;
669
+ isWithinMaintainScrollAtEndThreshold?: SharedValue<boolean>;
670
+ scrollOffset?: SharedValue<number>;
671
+ }
621
672
  interface AnimatedLegendListPropsBase<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit | "refScrollView"> {
622
673
  animatedProps?: ComponentProps<typeof Reanimated.ScrollView>["animatedProps"];
623
674
  refScrollView?: React.Ref<AnimatedScrollView>;
675
+ sharedValues?: AnimatedLegendListSharedValues;
624
676
  /**
625
677
  * Reanimated layout transition applied to each item container position view.
626
678
  * Example: `LinearTransition.duration(280)`.
@@ -634,4 +686,4 @@ type AnimatedLegendListDefinition = <ItemT>(props: AnimatedLegendListProps<ItemT
634
686
  }) => React.ReactElement | null;
635
687
  declare const AnimatedLegendList: AnimatedLegendListDefinition;
636
688
 
637
- export { AnimatedLegendList, type AnimatedLegendListProps, type AnimatedLegendListPropsBase };
689
+ export { AnimatedLegendList, type AnimatedLegendListProps, type AnimatedLegendListPropsBase, type AnimatedLegendListSharedValues };
package/reanimated.js CHANGED
@@ -29,9 +29,16 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
29
29
  var Reanimated__default = /*#__PURE__*/_interopDefault(Reanimated);
30
30
 
31
31
  // src/integrations/reanimated.tsx
32
- var { POSITION_OUT_OF_VIEW, IsNewArchitecture, useArr$, useCombinedRef, getComponent } = reactNative.internal;
32
+ var {
33
+ POSITION_OUT_OF_VIEW,
34
+ IsNewArchitecture,
35
+ getStickyPushLimit,
36
+ typedMemo,
37
+ useArr$,
38
+ useCombinedRef,
39
+ getComponent
40
+ } = reactNative.internal;
33
41
  var { peek$, useStateContext } = reactNative.internal;
34
- var typedMemo = React.memo;
35
42
  var ReanimatedScrollBridge = typedMemo(function ReanimatedScrollBridgeComponent({
36
43
  forwardedRef,
37
44
  scrollOffset,
@@ -67,18 +74,27 @@ var StickyOverlay = typedMemo(function StickyOverlayComponent({ stickyHeaderConf
67
74
  });
68
75
  var ReanimatedPositionViewSticky = typedMemo(function ReanimatedPositionViewStickyComponent(props) {
69
76
  var _a;
77
+ const ctx = useStateContext();
70
78
  const { id, horizontal, style, refView, stickyScrollOffset, stickyHeaderConfig, index, children, ...rest } = props;
71
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
79
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0, itemKey, _totalSize = 0] = useArr$([
72
80
  `containerPosition${id}`,
73
81
  "headerSize",
74
- "stylePaddingTop"
82
+ "stylePaddingTop",
83
+ `containerItemKey${id}`,
84
+ "totalSize"
75
85
  ]);
86
+ const pushLimit = React__namespace.useMemo(
87
+ () => getStickyPushLimit(ctx.state, index, itemKey),
88
+ [ctx.state, index, itemKey, _totalSize]
89
+ );
76
90
  const stickyOffset = (_a = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a : 0;
77
91
  const stickyStart = position + headerSize + stylePaddingTop - stickyOffset;
78
92
  const transformStyle = Reanimated.useAnimatedStyle(() => {
79
93
  const delta = Math.max(0, stickyScrollOffset.value - stickyStart);
80
- return horizontal ? { transform: [{ translateX: position + delta }] } : { transform: [{ translateY: position + delta }] };
81
- }, [horizontal, position, stickyStart]);
94
+ const stickyPosition = position + delta;
95
+ const resolvedPosition = pushLimit !== void 0 ? Math.min(stickyPosition, pushLimit) : stickyPosition;
96
+ return horizontal ? { transform: [{ translateX: resolvedPosition }] } : { transform: [{ translateY: resolvedPosition }] };
97
+ }, [horizontal, position, pushLimit, stickyStart]);
82
98
  const viewStyle = React__namespace.useMemo(
83
99
  () => [style, { zIndex: index + 1e3 }, transformStyle],
84
100
  [index, style, transformStyle]
@@ -116,18 +132,71 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
116
132
  children
117
133
  );
118
134
  });
135
+ function setSharedValueValue(sharedValue, value) {
136
+ if (!sharedValue) {
137
+ return;
138
+ }
139
+ const sharedValueWithMethods = sharedValue;
140
+ const currentValue = typeof sharedValueWithMethods.get === "function" ? sharedValueWithMethods.get() : sharedValueWithMethods.value;
141
+ if (currentValue === value) {
142
+ return;
143
+ }
144
+ if (typeof sharedValueWithMethods.set === "function") {
145
+ sharedValueWithMethods.set(value);
146
+ } else {
147
+ sharedValueWithMethods.value = value;
148
+ }
149
+ }
150
+ function useAnimatedLegendListSharedValuesSync(legendList, sharedValues) {
151
+ React__namespace.useEffect(() => {
152
+ if (!legendList || !sharedValues) {
153
+ return;
154
+ }
155
+ const state = legendList.getState();
156
+ setSharedValueValue(sharedValues.activeStickyIndex, state.activeStickyIndex);
157
+ setSharedValueValue(sharedValues.isAtEnd, state.isAtEnd);
158
+ setSharedValueValue(sharedValues.isAtStart, state.isAtStart);
159
+ setSharedValueValue(sharedValues.isNearEnd, state.isNearEnd);
160
+ setSharedValueValue(sharedValues.isNearStart, state.isNearStart);
161
+ setSharedValueValue(
162
+ sharedValues.isWithinMaintainScrollAtEndThreshold,
163
+ state.isWithinMaintainScrollAtEndThreshold
164
+ );
165
+ setSharedValueValue(sharedValues.scrollOffset, state.scroll);
166
+ const unsubscribers = [
167
+ sharedValues.activeStickyIndex ? state.listen(
168
+ "activeStickyIndex",
169
+ (value) => setSharedValueValue(sharedValues.activeStickyIndex, value)
170
+ ) : void 0,
171
+ sharedValues.isAtEnd ? state.listen("isAtEnd", (value) => setSharedValueValue(sharedValues.isAtEnd, value)) : void 0,
172
+ sharedValues.isAtStart ? state.listen("isAtStart", (value) => setSharedValueValue(sharedValues.isAtStart, value)) : void 0,
173
+ sharedValues.isNearEnd ? state.listen("isNearEnd", (value) => setSharedValueValue(sharedValues.isNearEnd, value)) : void 0,
174
+ sharedValues.isNearStart ? state.listen("isNearStart", (value) => setSharedValueValue(sharedValues.isNearStart, value)) : void 0,
175
+ sharedValues.isWithinMaintainScrollAtEndThreshold ? state.listen(
176
+ "isWithinMaintainScrollAtEndThreshold",
177
+ (value) => setSharedValueValue(sharedValues.isWithinMaintainScrollAtEndThreshold, value)
178
+ ) : void 0
179
+ ];
180
+ return () => {
181
+ for (const unsubscribe of unsubscribers) {
182
+ unsubscribe == null ? void 0 : unsubscribe();
183
+ }
184
+ };
185
+ }, [legendList, sharedValues]);
186
+ }
119
187
  var LegendListForwardedRef = typedMemo(
120
188
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
121
189
  React__namespace.forwardRef(function LegendListForwardedRef2(props, ref) {
122
- const { itemLayoutAnimation, recycleItems, refLegendList, renderScrollComponent, ...rest } = props;
190
+ var _a;
191
+ const { itemLayoutAnimation, recycleItems, refLegendList, renderScrollComponent, sharedValues, ...rest } = props;
123
192
  const refFn = React.useCallback(
124
193
  (r) => {
125
194
  refLegendList(r);
126
195
  },
127
196
  [refLegendList]
128
197
  );
129
- const stickyScrollOffset = Reanimated.useSharedValue(0);
130
- const shouldUseReanimatedScrollView = IsNewArchitecture;
198
+ const internalScrollOffset = Reanimated.useSharedValue(0);
199
+ const scrollOffset = (_a = sharedValues == null ? void 0 : sharedValues.scrollOffset) != null ? _a : internalScrollOffset;
131
200
  const renderScrollComponentForBridge = React__namespace.useMemo(
132
201
  () => renderScrollComponent ? (scrollViewProps) => renderScrollComponent(scrollViewProps) : void 0,
133
202
  [renderScrollComponent]
@@ -141,17 +210,17 @@ var LegendListForwardedRef = typedMemo(
141
210
  ...restScrollViewProps,
142
211
  forwardedRef,
143
212
  renderScrollComponent: renderScrollComponentForBridge,
144
- scrollOffset: stickyScrollOffset
213
+ scrollOffset
145
214
  }
146
215
  );
147
216
  },
148
- [renderScrollComponentForBridge, stickyScrollOffset]
217
+ [renderScrollComponentForBridge, scrollOffset]
149
218
  );
150
219
  const stickyPositionComponentInternal = React__namespace.useMemo(
151
220
  () => function StickyPositionComponent(stickyProps) {
152
- return /* @__PURE__ */ React__namespace.createElement(ReanimatedPositionViewSticky, { ...stickyProps, stickyScrollOffset });
221
+ return /* @__PURE__ */ React__namespace.createElement(ReanimatedPositionViewSticky, { ...stickyProps, stickyScrollOffset: scrollOffset });
153
222
  },
154
- [stickyScrollOffset]
223
+ [scrollOffset]
155
224
  );
156
225
  const itemLayoutAnimationRef = React__namespace.useRef(itemLayoutAnimation);
157
226
  itemLayoutAnimationRef.current = itemLayoutAnimation;
@@ -175,10 +244,10 @@ var LegendListForwardedRef = typedMemo(
175
244
  ...rest,
176
245
  positionComponentInternal,
177
246
  recycleItems,
178
- ...shouldUseReanimatedScrollView ? {
247
+ ...{
179
248
  renderScrollComponent: renderReanimatedScrollComponent,
180
- stickyPositionComponentInternal
181
- } : {}
249
+ ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}
250
+ }
182
251
  };
183
252
  return /* @__PURE__ */ React__namespace.createElement(reactNative.LegendList, { ref: refFn, refScrollView: ref, ...legendListProps });
184
253
  })
@@ -189,9 +258,15 @@ var AnimatedLegendList = typedMemo(
189
258
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
190
259
  React__namespace.forwardRef(function AnimatedLegendList2(props, ref) {
191
260
  const { refScrollView, ...rest } = props;
192
- const { animatedProps } = props;
193
- const refLegendList = React__namespace.useRef(null);
194
- const combinedRef = useCombinedRef(refLegendList, ref);
261
+ const { animatedProps, sharedValues } = props;
262
+ const [legendList, setLegendList] = React__namespace.useState(null);
263
+ const combinedRef = useCombinedRef(
264
+ React__namespace.useCallback((instance) => {
265
+ setLegendList((prev) => prev === instance ? prev : instance);
266
+ }, []),
267
+ ref
268
+ );
269
+ useAnimatedLegendListSharedValuesSync(legendList, sharedValues);
195
270
  const forwardedProps = {
196
271
  ...rest,
197
272
  animatedPropsInternal: animatedProps,
package/reanimated.mjs CHANGED
@@ -1,13 +1,20 @@
1
1
  import * as React from 'react';
2
- import { useCallback, memo } from 'react';
2
+ import { useCallback } from 'react';
3
3
  import { View } from 'react-native';
4
4
  import Reanimated, { useAnimatedRef, useScrollViewOffset, useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
5
5
  import { internal, LegendList } from '@legendapp/list/react-native';
6
6
 
7
7
  // src/integrations/reanimated.tsx
8
- var { POSITION_OUT_OF_VIEW, IsNewArchitecture, useArr$, useCombinedRef, getComponent } = internal;
8
+ var {
9
+ POSITION_OUT_OF_VIEW,
10
+ IsNewArchitecture,
11
+ getStickyPushLimit,
12
+ typedMemo,
13
+ useArr$,
14
+ useCombinedRef,
15
+ getComponent
16
+ } = internal;
9
17
  var { peek$, useStateContext } = internal;
10
- var typedMemo = memo;
11
18
  var ReanimatedScrollBridge = typedMemo(function ReanimatedScrollBridgeComponent({
12
19
  forwardedRef,
13
20
  scrollOffset,
@@ -43,18 +50,27 @@ var StickyOverlay = typedMemo(function StickyOverlayComponent({ stickyHeaderConf
43
50
  });
44
51
  var ReanimatedPositionViewSticky = typedMemo(function ReanimatedPositionViewStickyComponent(props) {
45
52
  var _a;
53
+ const ctx = useStateContext();
46
54
  const { id, horizontal, style, refView, stickyScrollOffset, stickyHeaderConfig, index, children, ...rest } = props;
47
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
55
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0, itemKey, _totalSize = 0] = useArr$([
48
56
  `containerPosition${id}`,
49
57
  "headerSize",
50
- "stylePaddingTop"
58
+ "stylePaddingTop",
59
+ `containerItemKey${id}`,
60
+ "totalSize"
51
61
  ]);
62
+ const pushLimit = React.useMemo(
63
+ () => getStickyPushLimit(ctx.state, index, itemKey),
64
+ [ctx.state, index, itemKey, _totalSize]
65
+ );
52
66
  const stickyOffset = (_a = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a : 0;
53
67
  const stickyStart = position + headerSize + stylePaddingTop - stickyOffset;
54
68
  const transformStyle = useAnimatedStyle(() => {
55
69
  const delta = Math.max(0, stickyScrollOffset.value - stickyStart);
56
- return horizontal ? { transform: [{ translateX: position + delta }] } : { transform: [{ translateY: position + delta }] };
57
- }, [horizontal, position, stickyStart]);
70
+ const stickyPosition = position + delta;
71
+ const resolvedPosition = pushLimit !== void 0 ? Math.min(stickyPosition, pushLimit) : stickyPosition;
72
+ return horizontal ? { transform: [{ translateX: resolvedPosition }] } : { transform: [{ translateY: resolvedPosition }] };
73
+ }, [horizontal, position, pushLimit, stickyStart]);
58
74
  const viewStyle = React.useMemo(
59
75
  () => [style, { zIndex: index + 1e3 }, transformStyle],
60
76
  [index, style, transformStyle]
@@ -92,18 +108,71 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
92
108
  children
93
109
  );
94
110
  });
111
+ function setSharedValueValue(sharedValue, value) {
112
+ if (!sharedValue) {
113
+ return;
114
+ }
115
+ const sharedValueWithMethods = sharedValue;
116
+ const currentValue = typeof sharedValueWithMethods.get === "function" ? sharedValueWithMethods.get() : sharedValueWithMethods.value;
117
+ if (currentValue === value) {
118
+ return;
119
+ }
120
+ if (typeof sharedValueWithMethods.set === "function") {
121
+ sharedValueWithMethods.set(value);
122
+ } else {
123
+ sharedValueWithMethods.value = value;
124
+ }
125
+ }
126
+ function useAnimatedLegendListSharedValuesSync(legendList, sharedValues) {
127
+ React.useEffect(() => {
128
+ if (!legendList || !sharedValues) {
129
+ return;
130
+ }
131
+ const state = legendList.getState();
132
+ setSharedValueValue(sharedValues.activeStickyIndex, state.activeStickyIndex);
133
+ setSharedValueValue(sharedValues.isAtEnd, state.isAtEnd);
134
+ setSharedValueValue(sharedValues.isAtStart, state.isAtStart);
135
+ setSharedValueValue(sharedValues.isNearEnd, state.isNearEnd);
136
+ setSharedValueValue(sharedValues.isNearStart, state.isNearStart);
137
+ setSharedValueValue(
138
+ sharedValues.isWithinMaintainScrollAtEndThreshold,
139
+ state.isWithinMaintainScrollAtEndThreshold
140
+ );
141
+ setSharedValueValue(sharedValues.scrollOffset, state.scroll);
142
+ const unsubscribers = [
143
+ sharedValues.activeStickyIndex ? state.listen(
144
+ "activeStickyIndex",
145
+ (value) => setSharedValueValue(sharedValues.activeStickyIndex, value)
146
+ ) : void 0,
147
+ sharedValues.isAtEnd ? state.listen("isAtEnd", (value) => setSharedValueValue(sharedValues.isAtEnd, value)) : void 0,
148
+ sharedValues.isAtStart ? state.listen("isAtStart", (value) => setSharedValueValue(sharedValues.isAtStart, value)) : void 0,
149
+ sharedValues.isNearEnd ? state.listen("isNearEnd", (value) => setSharedValueValue(sharedValues.isNearEnd, value)) : void 0,
150
+ sharedValues.isNearStart ? state.listen("isNearStart", (value) => setSharedValueValue(sharedValues.isNearStart, value)) : void 0,
151
+ sharedValues.isWithinMaintainScrollAtEndThreshold ? state.listen(
152
+ "isWithinMaintainScrollAtEndThreshold",
153
+ (value) => setSharedValueValue(sharedValues.isWithinMaintainScrollAtEndThreshold, value)
154
+ ) : void 0
155
+ ];
156
+ return () => {
157
+ for (const unsubscribe of unsubscribers) {
158
+ unsubscribe == null ? void 0 : unsubscribe();
159
+ }
160
+ };
161
+ }, [legendList, sharedValues]);
162
+ }
95
163
  var LegendListForwardedRef = typedMemo(
96
164
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
97
165
  React.forwardRef(function LegendListForwardedRef2(props, ref) {
98
- const { itemLayoutAnimation, recycleItems, refLegendList, renderScrollComponent, ...rest } = props;
166
+ var _a;
167
+ const { itemLayoutAnimation, recycleItems, refLegendList, renderScrollComponent, sharedValues, ...rest } = props;
99
168
  const refFn = useCallback(
100
169
  (r) => {
101
170
  refLegendList(r);
102
171
  },
103
172
  [refLegendList]
104
173
  );
105
- const stickyScrollOffset = useSharedValue(0);
106
- const shouldUseReanimatedScrollView = IsNewArchitecture;
174
+ const internalScrollOffset = useSharedValue(0);
175
+ const scrollOffset = (_a = sharedValues == null ? void 0 : sharedValues.scrollOffset) != null ? _a : internalScrollOffset;
107
176
  const renderScrollComponentForBridge = React.useMemo(
108
177
  () => renderScrollComponent ? (scrollViewProps) => renderScrollComponent(scrollViewProps) : void 0,
109
178
  [renderScrollComponent]
@@ -117,17 +186,17 @@ var LegendListForwardedRef = typedMemo(
117
186
  ...restScrollViewProps,
118
187
  forwardedRef,
119
188
  renderScrollComponent: renderScrollComponentForBridge,
120
- scrollOffset: stickyScrollOffset
189
+ scrollOffset
121
190
  }
122
191
  );
123
192
  },
124
- [renderScrollComponentForBridge, stickyScrollOffset]
193
+ [renderScrollComponentForBridge, scrollOffset]
125
194
  );
126
195
  const stickyPositionComponentInternal = React.useMemo(
127
196
  () => function StickyPositionComponent(stickyProps) {
128
- return /* @__PURE__ */ React.createElement(ReanimatedPositionViewSticky, { ...stickyProps, stickyScrollOffset });
197
+ return /* @__PURE__ */ React.createElement(ReanimatedPositionViewSticky, { ...stickyProps, stickyScrollOffset: scrollOffset });
129
198
  },
130
- [stickyScrollOffset]
199
+ [scrollOffset]
131
200
  );
132
201
  const itemLayoutAnimationRef = React.useRef(itemLayoutAnimation);
133
202
  itemLayoutAnimationRef.current = itemLayoutAnimation;
@@ -151,10 +220,10 @@ var LegendListForwardedRef = typedMemo(
151
220
  ...rest,
152
221
  positionComponentInternal,
153
222
  recycleItems,
154
- ...shouldUseReanimatedScrollView ? {
223
+ ...{
155
224
  renderScrollComponent: renderReanimatedScrollComponent,
156
- stickyPositionComponentInternal
157
- } : {}
225
+ ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}
226
+ }
158
227
  };
159
228
  return /* @__PURE__ */ React.createElement(LegendList, { ref: refFn, refScrollView: ref, ...legendListProps });
160
229
  })
@@ -165,9 +234,15 @@ var AnimatedLegendList = typedMemo(
165
234
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
166
235
  React.forwardRef(function AnimatedLegendList2(props, ref) {
167
236
  const { refScrollView, ...rest } = props;
168
- const { animatedProps } = props;
169
- const refLegendList = React.useRef(null);
170
- const combinedRef = useCombinedRef(refLegendList, ref);
237
+ const { animatedProps, sharedValues } = props;
238
+ const [legendList, setLegendList] = React.useState(null);
239
+ const combinedRef = useCombinedRef(
240
+ React.useCallback((instance) => {
241
+ setLegendList((prev) => prev === instance ? prev : instance);
242
+ }, []),
243
+ ref
244
+ );
245
+ useAnimatedLegendListSharedValuesSync(legendList, sharedValues);
171
246
  const forwardedProps = {
172
247
  ...rest,
173
248
  animatedPropsInternal: animatedProps,