@legendapp/list 3.6.0 → 3.7.0

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
@@ -9,8 +9,8 @@ interface MaintainVisibleContentPositionNormalized<ItemT = any> {
9
9
  shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
10
10
  }
11
11
 
12
- type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "containerLayoutEpoch" | "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" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemIndex${number}` | `containerItemKey${number}` | `containerLayoutReady${number}` | `containerPosition${number}` | `containerSticky${number}`;
13
- type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
12
+ type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "containerLayoutEpoch" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "isScrollActive" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isFollowingEnd" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemIndex${number}` | `containerItemKey${number}` | `containerLayoutReady${number}` | `containerPosition${number}` | `containerSticky${number}`;
13
+ type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isFollowingEnd" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
14
14
  type ListenerTypeValueMap = {
15
15
  activeStickyIndex: number;
16
16
  alignItemsAtEndPadding: number;
@@ -22,6 +22,7 @@ type ListenerTypeValueMap = {
22
22
  extraData: any;
23
23
  footerSize: number;
24
24
  headerSize: number;
25
+ isFollowingEnd: boolean;
25
26
  isAtEnd: boolean;
26
27
  isAtStart: boolean;
27
28
  isNearEnd: boolean;
@@ -35,6 +36,7 @@ type ListenerTypeValueMap = {
35
36
  numContainersPooled: number;
36
37
  otherAxisSize: number;
37
38
  readyToRender: boolean;
39
+ isScrollActive: boolean;
38
40
  scrollAdjust: number;
39
41
  scrollAdjustPending: number;
40
42
  scrollAdjustUserOffset: number;
@@ -200,6 +202,7 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
200
202
  extraData?: any;
201
203
  /**
202
204
  * In case items always have a fixed size, you can provide a function to return it.
205
+ * Change extraData when the fixed sizes returned by this callback change.
203
206
  */
204
207
  getFixedItemSize?: (item: ItemT, index: number, type: TItemType) => number | undefined;
205
208
  /**
@@ -272,6 +275,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
272
275
  /**
273
276
  * If true, auto-scrolls to end when new items are added.
274
277
  * Use an options object to opt into specific triggers and control whether that scroll is animated.
278
+ * Setting false pauses maintenance while retaining end-follow intent. Re-enabling resumes
279
+ * that intent even if content grew beyond the threshold; user or list navigation releases it.
280
+ * While paused, isFollowingEnd reports the retained intent.
281
+ * With a native screen reader enabled, unowned accessibility scrolls toward the start
282
+ * release following. Assistive-tech scrolls during library animations are not detected.
275
283
  * @default false
276
284
  */
277
285
  maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
@@ -282,11 +290,13 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
282
290
  maintainScrollAtEndThreshold?: number;
283
291
  /**
284
292
  * Maintains visibility of content.
285
- * - scroll (default: true) stabilizes during size/layout changes while scrolling.
286
- * - data (default: false) stabilizes when the data array changes; passing true also sets the RN maintainVisibleContentPosition prop.
293
+ * - size (default: true) stabilizes during size/layout changes while scrolling.
294
+ * - data (default: false) stabilizes when the data array changes.
287
295
  * - shouldRestorePosition can opt out specific items from data-change anchoring.
288
296
  * - undefined (default) enables scroll stabilization but skips data-change anchoring.
289
- * - true enables both behaviors; false disables both.
297
+ * - true enables both JS behaviors; false disables both.
298
+ * Native iOS maintainVisibleContentPosition is enabled when either size or data stabilization is enabled,
299
+ * and disabled when both are disabled.
290
300
  */
291
301
  maintainVisibleContentPosition?: boolean | MaintainVisibleContentPositionConfig<ItemT>;
292
302
  /**
@@ -378,8 +388,25 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
378
388
  */
379
389
  onRefresh?: () => void;
380
390
  /**
381
- * Called when the list scrolls.
382
- */
391
+ * Called once per accepted user gesture, in either direction: native drag (including
392
+ * momentum), web wheel/touch movement, non-repeated scroll keydown, or scrollbar drag.
393
+ * Wheel input at a boundary is ignored unless navigation is active. Without wheel
394
+ * momentum metadata, gesture boundaries are inferred from timing, direction and force.
395
+ * iOS status-bar scrolling emits on native onScrollToTop unless accessibility motion
396
+ * already reported it during the animation.
397
+ * With a native screen reader enabled, otherwise unowned scroll motion with stable
398
+ * content/viewport size is inferred as accessibility input (including focus navigation),
399
+ * once per burst; motion toward the start releases end following. Library animations
400
+ * and controller/native-ref command windows mask assistive-tech scrolling.
401
+ * Native commands bypassing the list and its refs (e.g. Reanimated scrollTo worklets)
402
+ * cannot be distinguished from accessibility input while a screen reader is on; call
403
+ * cancelScroll() on the JS thread before that navigation to release end following.
404
+ * Layout/clamp/MVCP events and commands through the list or its refs do not emit it.
405
+ * @experimental Native pointer/wheel-only input without a screen reader is not detected.
406
+ * Web scrollbar detection requires pointer events in a measurable, non-overlay gutter.
407
+ */
408
+ onUserScroll?: () => void;
409
+ /** Called when the list scrolls. */
383
410
  onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
384
411
  /**
385
412
  * Called when scrolling reaches the start within onStartReachedThreshold.
@@ -511,6 +538,8 @@ interface AlwaysRenderConfig {
511
538
  keys?: string[];
512
539
  }
513
540
  interface MaintainScrollAtEndOnOptions {
541
+ /** Follow only additions that preserve every existing item key in order. */
542
+ dataAppend?: boolean;
514
543
  dataChange?: boolean;
515
544
  footerLayout?: boolean;
516
545
  itemLayout?: boolean;
@@ -552,9 +581,13 @@ type LegendListState = {
552
581
  activeStickyIndex: number;
553
582
  contentLength: number;
554
583
  data: readonly any[];
584
+ dataKey?: Key;
555
585
  elementAtIndex: (index: number) => any;
556
586
  end: number;
557
587
  endBuffered: number;
588
+ /** Distance to the physical scroll edge, including the trailing inset. */
589
+ distanceFromEnd: number;
590
+ isFollowingEnd: boolean;
558
591
  isAtEnd: boolean;
559
592
  isAtStart: boolean;
560
593
  isNearEnd: boolean;
@@ -564,7 +597,10 @@ type LegendListState = {
564
597
  isWithinMaintainScrollAtEndThreshold: boolean;
565
598
  getAverageItemSizes: () => Record<string, LegendListAverageItemSize>;
566
599
  indexByKey: (key: string) => number | undefined;
567
- listen: <T extends LegendListListenerType>(listenerType: T, callback: (value: ListenerTypeValueMap[T]) => void) => () => void;
600
+ listen: {
601
+ <T extends Exclude<LegendListListenerType, "isFollowingEnd">>(listenerType: T, callback: (value: Pick<ListenerTypeValueMap, LegendListListenerType>[T]) => void): () => void;
602
+ <T extends LegendListListenerType>(listenerType: T, callback: (value: Pick<ListenerTypeValueMap, LegendListListenerType>[T]) => void): () => void;
603
+ };
568
604
  listenToPosition: (key: string, callback: (value: number) => void) => () => void;
569
605
  positionAtIndex: (index: number) => number;
570
606
  positionByKey: (key: string) => number | undefined;
@@ -577,6 +613,28 @@ type LegendListState = {
577
613
  startBuffered: number;
578
614
  };
579
615
  type LegendListRef$1 = {
616
+ /** Cancels queued and active scrolling, settles its promise, and stops at the observed offset. */
617
+ cancelScroll(): void;
618
+ /** Scrolls by stable item identity through prepends; resolves without scrolling if the key is missing or removed. */
619
+ scrollToKey(params: {
620
+ key: string;
621
+ animated?: boolean;
622
+ viewOffset?: number;
623
+ viewPosition?: number;
624
+ }): Promise<void>;
625
+ /**
626
+ * Captures an item's key and viewport offset, holding it through layout changes by default.
627
+ * Omit key to use the first visible item; set hold to false to read without side effects.
628
+ * Returns undefined before initial placement, during dataset replacement, or if no item is found.
629
+ * While following at the end, returns the anchor without interrupting end following.
630
+ */
631
+ anchorVisibleContent(options?: {
632
+ key?: string;
633
+ hold?: boolean;
634
+ }): {
635
+ key: string;
636
+ offset: number;
637
+ } | undefined;
580
638
  /**
581
639
  * Clears internal virtualization caches.
582
640
  * @param options - Cache clearing options.
@@ -619,20 +677,40 @@ type LegendListRef$1 = {
619
677
  * @param params - Parameters for scrolling.
620
678
  * @param params.animated - If true, animates the scroll. Default: true.
621
679
  * @param params.index - The index to scroll to.
680
+ * @param params.itemOffset - Region start measured from the item's start along the scroll axis. Default: 0 when itemLength is provided.
681
+ * @param params.itemLength - Region length along the scroll axis. Default: 0 when itemOffset is provided. Omit both to reveal the whole item.
682
+ * @param params.viewportMargin - Space to keep clear at the start and end of the viewport along the scroll axis.
683
+ * The region should be within the item. A visible region does not scroll; otherwise its nearest edge aligns inside the margins. A region larger than the available space aligns its start.
622
684
  */
623
685
  scrollIndexIntoView(params: {
624
686
  animated?: boolean | undefined;
625
687
  index: number;
688
+ itemOffset?: number;
689
+ itemLength?: number;
690
+ viewportMargin?: {
691
+ start?: number;
692
+ end?: number;
693
+ };
626
694
  }): Promise<void>;
627
695
  /**
628
- * Scrolls a specific index into view.
696
+ * Scrolls a specific item into view.
629
697
  * @param params - Parameters for scrolling.
630
698
  * @param params.animated - If true, animates the scroll. Default: true.
631
699
  * @param params.item - The item to scroll to.
700
+ * @param params.itemOffset - Region start measured from the item's start along the scroll axis. Default: 0 when itemLength is provided.
701
+ * @param params.itemLength - Region length along the scroll axis. Default: 0 when itemOffset is provided. Omit both to reveal the whole item.
702
+ * @param params.viewportMargin - Space to keep clear at the start and end of the viewport along the scroll axis.
703
+ * The region should be within the item. A visible region does not scroll; otherwise its nearest edge aligns inside the margins. A region larger than the available space aligns its start.
632
704
  */
633
705
  scrollItemIntoView(params: {
634
706
  animated?: boolean | undefined;
635
707
  item: any;
708
+ itemOffset?: number;
709
+ itemLength?: number;
710
+ viewportMargin?: {
711
+ start?: number;
712
+ end?: number;
713
+ };
636
714
  }): Promise<void>;
637
715
  /**
638
716
  * Scrolls to the end of the list.
@@ -762,7 +840,9 @@ type LegendListPropsOverrides<ItemT, TItemType extends string | undefined> = Omi
762
840
  };
763
841
  type LegendListProps<ItemT = any, TItemType extends string | undefined = string | undefined> = LegendListPropsOverrides<ItemT, TItemType>;
764
842
  type LegendListRef = Omit<LegendListRef$1, "getAnimatableRef" | "getNativeScrollRef" | "getScrollResponder" | "reportContentInset"> & {
843
+ /** Call cancelScroll() before scrolling it directly so end following and list scrolling stop first. */
765
844
  getAnimatableRef(): React.ElementRef<typeof ScrollViewComponent>;
845
+ /** Call cancelScroll() before scrolling it directly so end following and list scrolling stop first. */
766
846
  getNativeScrollRef(): React.ElementRef<typeof ScrollViewComponent>;
767
847
  getScrollResponder(): ScrollResponderMixin;
768
848
  reportContentInset(inset?: Partial<Insets$1> | null): void;
package/reanimated.js CHANGED
@@ -112,16 +112,16 @@ var ReanimatedPositionViewSticky = typedMemo(function ReanimatedPositionViewStic
112
112
  });
113
113
  var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(props) {
114
114
  const ctx = useStateContext();
115
- const { id, horizontal, style, refView, children, recycleItems, layoutTransition, ...rest } = props;
116
- const [positionValue = POSITION_OUT_OF_VIEW, layoutReady = true] = useArr$([
115
+ const { id, horizontal, style, refView, children, layoutTransition, ...rest } = props;
116
+ const [positionValue = POSITION_OUT_OF_VIEW, layoutReady = true, isScrollActive] = useArr$([
117
117
  `containerPosition${id}`,
118
- `containerLayoutReady${id}`
118
+ `containerLayoutReady${id}`,
119
+ "isScrollActive"
119
120
  ]);
120
121
  const prevItemKeyRef = React__namespace.useRef(void 0);
121
122
  let shouldSkipTransitionForRecycleReuse = false;
122
- if (recycleItems && layoutTransition) {
123
- const itemKeySignal = `containerItemKey${id}`;
124
- const itemKey = peek$(ctx, itemKeySignal);
123
+ if (layoutTransition) {
124
+ const itemKey = peek$(ctx, `containerItemKey${id}`);
125
125
  shouldSkipTransitionForRecycleReuse = itemKey !== void 0 && prevItemKeyRef.current !== void 0 && prevItemKeyRef.current !== itemKey;
126
126
  if (itemKey !== void 0) {
127
127
  prevItemKeyRef.current = itemKey;
@@ -129,6 +129,10 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
129
129
  } else {
130
130
  prevItemKeyRef.current = void 0;
131
131
  }
132
+ const previousAdjustment = React__namespace.useRef(ctx.state.layoutAdjustmentEpoch);
133
+ const wasAdjusted = previousAdjustment.current !== ctx.state.layoutAdjustmentEpoch;
134
+ previousAdjustment.current = ctx.state.layoutAdjustmentEpoch;
135
+ const shouldSkipLayoutTransition = wasAdjusted || isScrollActive || !!ctx.state.scrollingTo;
132
136
  const viewStyle = React__namespace.useMemo(
133
137
  () => [style, horizontal ? { left: positionValue } : { top: positionValue }, !layoutReady && { opacity: 0 }],
134
138
  [horizontal, layoutReady, positionValue, style]
@@ -136,7 +140,7 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
136
140
  return /* @__PURE__ */ React__namespace.createElement(
137
141
  Reanimated__default.default.View,
138
142
  {
139
- layout: shouldSkipTransitionForRecycleReuse ? void 0 : layoutTransition,
143
+ layout: shouldSkipTransitionForRecycleReuse || shouldSkipLayoutTransition ? void 0 : layoutTransition,
140
144
  ref: refView,
141
145
  style: viewStyle,
142
146
  ...rest,
@@ -197,13 +201,7 @@ var LegendListForwardedRef = typedMemo(
197
201
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
198
202
  React__namespace.forwardRef(function LegendListForwardedRef2(props, ref) {
199
203
  var _a, _b;
200
- const { itemLayoutAnimation, recycleItems, refLegendList, renderScrollComponent, sharedValues, ...rest } = props;
201
- const refFn = React.useCallback(
202
- (r) => {
203
- refLegendList(r);
204
- },
205
- [refLegendList]
206
- );
204
+ const { itemLayoutAnimation, refLegendList, renderScrollComponent, sharedValues, ...rest } = props;
207
205
  const internalScrollOffset = Reanimated.useSharedValue(0);
208
206
  const scrollOffset = (_a = sharedValues == null ? void 0 : sharedValues.scrollOffset) != null ? _a : internalScrollOffset;
209
207
  const trackedScrollOffset = (sharedValues == null ? void 0 : sharedValues.scrollOffset) !== void 0 || ((_b = rest.stickyHeaderIndices) == null ? void 0 : _b.length) ? scrollOffset : void 0;
@@ -239,26 +237,16 @@ var LegendListForwardedRef = typedMemo(
239
237
  return void 0;
240
238
  }
241
239
  return function PositionComponent(positionProps) {
242
- return /* @__PURE__ */ React__namespace.createElement(
243
- ReanimatedPositionView,
244
- {
245
- ...positionProps,
246
- layoutTransition: itemLayoutAnimationRef.current,
247
- recycleItems
248
- }
249
- );
240
+ return /* @__PURE__ */ React__namespace.createElement(ReanimatedPositionView, { ...positionProps, layoutTransition: itemLayoutAnimationRef.current });
250
241
  };
251
- }, [hasItemLayoutAnimation, recycleItems]);
242
+ }, [hasItemLayoutAnimation]);
252
243
  const legendListProps = {
253
244
  ...rest,
254
245
  positionComponentInternal,
255
- recycleItems,
256
- ...{
257
- renderScrollComponent: renderReanimatedScrollComponent,
258
- ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}
259
- }
246
+ renderScrollComponent: renderReanimatedScrollComponent,
247
+ ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}
260
248
  };
261
- return /* @__PURE__ */ React__namespace.createElement(reactNative.LegendList, { ref: refFn, refScrollView: ref, ...legendListProps });
249
+ return /* @__PURE__ */ React__namespace.createElement(reactNative.LegendList, { ref: refLegendList, refScrollView: ref, ...legendListProps });
262
250
  })
263
251
  );
264
252
  var AnimatedLegendListComponent = Reanimated__default.default.createAnimatedComponent(LegendListForwardedRef);
package/reanimated.mjs CHANGED
@@ -88,16 +88,16 @@ var ReanimatedPositionViewSticky = typedMemo(function ReanimatedPositionViewStic
88
88
  });
89
89
  var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(props) {
90
90
  const ctx = useStateContext();
91
- const { id, horizontal, style, refView, children, recycleItems, layoutTransition, ...rest } = props;
92
- const [positionValue = POSITION_OUT_OF_VIEW, layoutReady = true] = useArr$([
91
+ const { id, horizontal, style, refView, children, layoutTransition, ...rest } = props;
92
+ const [positionValue = POSITION_OUT_OF_VIEW, layoutReady = true, isScrollActive] = useArr$([
93
93
  `containerPosition${id}`,
94
- `containerLayoutReady${id}`
94
+ `containerLayoutReady${id}`,
95
+ "isScrollActive"
95
96
  ]);
96
97
  const prevItemKeyRef = React.useRef(void 0);
97
98
  let shouldSkipTransitionForRecycleReuse = false;
98
- if (recycleItems && layoutTransition) {
99
- const itemKeySignal = `containerItemKey${id}`;
100
- const itemKey = peek$(ctx, itemKeySignal);
99
+ if (layoutTransition) {
100
+ const itemKey = peek$(ctx, `containerItemKey${id}`);
101
101
  shouldSkipTransitionForRecycleReuse = itemKey !== void 0 && prevItemKeyRef.current !== void 0 && prevItemKeyRef.current !== itemKey;
102
102
  if (itemKey !== void 0) {
103
103
  prevItemKeyRef.current = itemKey;
@@ -105,6 +105,10 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
105
105
  } else {
106
106
  prevItemKeyRef.current = void 0;
107
107
  }
108
+ const previousAdjustment = React.useRef(ctx.state.layoutAdjustmentEpoch);
109
+ const wasAdjusted = previousAdjustment.current !== ctx.state.layoutAdjustmentEpoch;
110
+ previousAdjustment.current = ctx.state.layoutAdjustmentEpoch;
111
+ const shouldSkipLayoutTransition = wasAdjusted || isScrollActive || !!ctx.state.scrollingTo;
108
112
  const viewStyle = React.useMemo(
109
113
  () => [style, horizontal ? { left: positionValue } : { top: positionValue }, !layoutReady && { opacity: 0 }],
110
114
  [horizontal, layoutReady, positionValue, style]
@@ -112,7 +116,7 @@ var ReanimatedPositionView = typedMemo(function ReanimatedPositionViewComponent(
112
116
  return /* @__PURE__ */ React.createElement(
113
117
  Reanimated.View,
114
118
  {
115
- layout: shouldSkipTransitionForRecycleReuse ? void 0 : layoutTransition,
119
+ layout: shouldSkipTransitionForRecycleReuse || shouldSkipLayoutTransition ? void 0 : layoutTransition,
116
120
  ref: refView,
117
121
  style: viewStyle,
118
122
  ...rest,
@@ -173,13 +177,7 @@ var LegendListForwardedRef = typedMemo(
173
177
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
174
178
  React.forwardRef(function LegendListForwardedRef2(props, ref) {
175
179
  var _a, _b;
176
- const { itemLayoutAnimation, recycleItems, refLegendList, renderScrollComponent, sharedValues, ...rest } = props;
177
- const refFn = useCallback(
178
- (r) => {
179
- refLegendList(r);
180
- },
181
- [refLegendList]
182
- );
180
+ const { itemLayoutAnimation, refLegendList, renderScrollComponent, sharedValues, ...rest } = props;
183
181
  const internalScrollOffset = useSharedValue(0);
184
182
  const scrollOffset = (_a = sharedValues == null ? void 0 : sharedValues.scrollOffset) != null ? _a : internalScrollOffset;
185
183
  const trackedScrollOffset = (sharedValues == null ? void 0 : sharedValues.scrollOffset) !== void 0 || ((_b = rest.stickyHeaderIndices) == null ? void 0 : _b.length) ? scrollOffset : void 0;
@@ -215,26 +213,16 @@ var LegendListForwardedRef = typedMemo(
215
213
  return void 0;
216
214
  }
217
215
  return function PositionComponent(positionProps) {
218
- return /* @__PURE__ */ React.createElement(
219
- ReanimatedPositionView,
220
- {
221
- ...positionProps,
222
- layoutTransition: itemLayoutAnimationRef.current,
223
- recycleItems
224
- }
225
- );
216
+ return /* @__PURE__ */ React.createElement(ReanimatedPositionView, { ...positionProps, layoutTransition: itemLayoutAnimationRef.current });
226
217
  };
227
- }, [hasItemLayoutAnimation, recycleItems]);
218
+ }, [hasItemLayoutAnimation]);
228
219
  const legendListProps = {
229
220
  ...rest,
230
221
  positionComponentInternal,
231
- recycleItems,
232
- ...{
233
- renderScrollComponent: renderReanimatedScrollComponent,
234
- ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}
235
- }
222
+ renderScrollComponent: renderReanimatedScrollComponent,
223
+ ...IsNewArchitecture ? { stickyPositionComponentInternal } : {}
236
224
  };
237
- return /* @__PURE__ */ React.createElement(LegendList, { ref: refFn, refScrollView: ref, ...legendListProps });
225
+ return /* @__PURE__ */ React.createElement(LegendList, { ref: refLegendList, refScrollView: ref, ...legendListProps });
238
226
  })
239
227
  );
240
228
  var AnimatedLegendListComponent = Reanimated.createAnimatedComponent(LegendListForwardedRef);
package/section-list.d.ts CHANGED
@@ -65,8 +65,8 @@ interface MaintainVisibleContentPositionNormalized<ItemT = any> {
65
65
  shouldRestorePosition?: (item: ItemT, index: number, data: readonly ItemT[]) => boolean;
66
66
  }
67
67
 
68
- type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "containerLayoutEpoch" | "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" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemIndex${number}` | `containerItemKey${number}` | `containerLayoutReady${number}` | `containerPosition${number}` | `containerSticky${number}`;
69
- type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
68
+ type ListenerType = "activeStickyIndex" | "alignItemsAtEndPadding" | "anchoredEndSpaceSize" | "containerLayoutEpoch" | "debugComputedScroll" | "debugRawScroll" | "extraData" | "footerSize" | "headerSize" | "lastItemKeys" | "lastPositionUpdate" | "maintainVisibleContentPosition" | "numColumns" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "isScrollActive" | "scrollAdjust" | "scrollAdjustPending" | "scrollAdjustUserOffset" | "scrollSize" | "snapToOffsets" | "stylePaddingTop" | "totalSize" | "isFollowingEnd" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | `containerColumn${number}` | `containerSpan${number}` | `containerItemData${number}` | `containerItemIndex${number}` | `containerItemKey${number}` | `containerLayoutReady${number}` | `containerPosition${number}` | `containerSticky${number}`;
69
+ type LegendListListenerType = Extract<ListenerType, "activeStickyIndex" | "anchoredEndSpaceSize" | "footerSize" | "headerSize" | "isFollowingEnd" | "isAtEnd" | "isAtStart" | "isNearEnd" | "isNearStart" | "isWithinMaintainScrollAtEndThreshold" | "adaptiveRender" | "lastItemKeys" | "lastPositionUpdate" | "numContainers" | "numContainersPooled" | "otherAxisSize" | "readyToRender" | "snapToOffsets" | "totalSize">;
70
70
  type ListenerTypeValueMap = {
71
71
  activeStickyIndex: number;
72
72
  alignItemsAtEndPadding: number;
@@ -78,6 +78,7 @@ type ListenerTypeValueMap = {
78
78
  extraData: any;
79
79
  footerSize: number;
80
80
  headerSize: number;
81
+ isFollowingEnd: boolean;
81
82
  isAtEnd: boolean;
82
83
  isAtStart: boolean;
83
84
  isNearEnd: boolean;
@@ -91,6 +92,7 @@ type ListenerTypeValueMap = {
91
92
  numContainersPooled: number;
92
93
  otherAxisSize: number;
93
94
  readyToRender: boolean;
95
+ isScrollActive: boolean;
94
96
  scrollAdjust: number;
95
97
  scrollAdjustPending: number;
96
98
  scrollAdjustUserOffset: number;
@@ -256,6 +258,7 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
256
258
  extraData?: any;
257
259
  /**
258
260
  * In case items always have a fixed size, you can provide a function to return it.
261
+ * Change extraData when the fixed sizes returned by this callback change.
259
262
  */
260
263
  getFixedItemSize?: (item: ItemT, index: number, type: TItemType) => number | undefined;
261
264
  /**
@@ -328,6 +331,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
328
331
  /**
329
332
  * If true, auto-scrolls to end when new items are added.
330
333
  * Use an options object to opt into specific triggers and control whether that scroll is animated.
334
+ * Setting false pauses maintenance while retaining end-follow intent. Re-enabling resumes
335
+ * that intent even if content grew beyond the threshold; user or list navigation releases it.
336
+ * While paused, isFollowingEnd reports the retained intent.
337
+ * With a native screen reader enabled, unowned accessibility scrolls toward the start
338
+ * release following. Assistive-tech scrolls during library animations are not detected.
331
339
  * @default false
332
340
  */
333
341
  maintainScrollAtEnd?: boolean | MaintainScrollAtEndOptions;
@@ -338,11 +346,13 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
338
346
  maintainScrollAtEndThreshold?: number;
339
347
  /**
340
348
  * Maintains visibility of content.
341
- * - scroll (default: true) stabilizes during size/layout changes while scrolling.
342
- * - data (default: false) stabilizes when the data array changes; passing true also sets the RN maintainVisibleContentPosition prop.
349
+ * - size (default: true) stabilizes during size/layout changes while scrolling.
350
+ * - data (default: false) stabilizes when the data array changes.
343
351
  * - shouldRestorePosition can opt out specific items from data-change anchoring.
344
352
  * - undefined (default) enables scroll stabilization but skips data-change anchoring.
345
- * - true enables both behaviors; false disables both.
353
+ * - true enables both JS behaviors; false disables both.
354
+ * Native iOS maintainVisibleContentPosition is enabled when either size or data stabilization is enabled,
355
+ * and disabled when both are disabled.
346
356
  */
347
357
  maintainVisibleContentPosition?: boolean | MaintainVisibleContentPositionConfig<ItemT>;
348
358
  /**
@@ -434,8 +444,25 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
434
444
  */
435
445
  onRefresh?: () => void;
436
446
  /**
437
- * Called when the list scrolls.
438
- */
447
+ * Called once per accepted user gesture, in either direction: native drag (including
448
+ * momentum), web wheel/touch movement, non-repeated scroll keydown, or scrollbar drag.
449
+ * Wheel input at a boundary is ignored unless navigation is active. Without wheel
450
+ * momentum metadata, gesture boundaries are inferred from timing, direction and force.
451
+ * iOS status-bar scrolling emits on native onScrollToTop unless accessibility motion
452
+ * already reported it during the animation.
453
+ * With a native screen reader enabled, otherwise unowned scroll motion with stable
454
+ * content/viewport size is inferred as accessibility input (including focus navigation),
455
+ * once per burst; motion toward the start releases end following. Library animations
456
+ * and controller/native-ref command windows mask assistive-tech scrolling.
457
+ * Native commands bypassing the list and its refs (e.g. Reanimated scrollTo worklets)
458
+ * cannot be distinguished from accessibility input while a screen reader is on; call
459
+ * cancelScroll() on the JS thread before that navigation to release end following.
460
+ * Layout/clamp/MVCP events and commands through the list or its refs do not emit it.
461
+ * @experimental Native pointer/wheel-only input without a screen reader is not detected.
462
+ * Web scrollbar detection requires pointer events in a measurable, non-overlay gutter.
463
+ */
464
+ onUserScroll?: () => void;
465
+ /** Called when the list scrolls. */
439
466
  onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
440
467
  /**
441
468
  * Called when scrolling reaches the start within onStartReachedThreshold.
@@ -567,6 +594,8 @@ interface AlwaysRenderConfig {
567
594
  keys?: string[];
568
595
  }
569
596
  interface MaintainScrollAtEndOnOptions {
597
+ /** Follow only additions that preserve every existing item key in order. */
598
+ dataAppend?: boolean;
570
599
  dataChange?: boolean;
571
600
  footerLayout?: boolean;
572
601
  itemLayout?: boolean;
@@ -608,9 +637,13 @@ type LegendListState = {
608
637
  activeStickyIndex: number;
609
638
  contentLength: number;
610
639
  data: readonly any[];
640
+ dataKey?: Key;
611
641
  elementAtIndex: (index: number) => any;
612
642
  end: number;
613
643
  endBuffered: number;
644
+ /** Distance to the physical scroll edge, including the trailing inset. */
645
+ distanceFromEnd: number;
646
+ isFollowingEnd: boolean;
614
647
  isAtEnd: boolean;
615
648
  isAtStart: boolean;
616
649
  isNearEnd: boolean;
@@ -620,7 +653,10 @@ type LegendListState = {
620
653
  isWithinMaintainScrollAtEndThreshold: boolean;
621
654
  getAverageItemSizes: () => Record<string, LegendListAverageItemSize>;
622
655
  indexByKey: (key: string) => number | undefined;
623
- listen: <T extends LegendListListenerType>(listenerType: T, callback: (value: ListenerTypeValueMap[T]) => void) => () => void;
656
+ listen: {
657
+ <T extends Exclude<LegendListListenerType, "isFollowingEnd">>(listenerType: T, callback: (value: Pick<ListenerTypeValueMap, LegendListListenerType>[T]) => void): () => void;
658
+ <T extends LegendListListenerType>(listenerType: T, callback: (value: Pick<ListenerTypeValueMap, LegendListListenerType>[T]) => void): () => void;
659
+ };
624
660
  listenToPosition: (key: string, callback: (value: number) => void) => () => void;
625
661
  positionAtIndex: (index: number) => number;
626
662
  positionByKey: (key: string) => number | undefined;
@@ -633,6 +669,28 @@ type LegendListState = {
633
669
  startBuffered: number;
634
670
  };
635
671
  type LegendListRef$1 = {
672
+ /** Cancels queued and active scrolling, settles its promise, and stops at the observed offset. */
673
+ cancelScroll(): void;
674
+ /** Scrolls by stable item identity through prepends; resolves without scrolling if the key is missing or removed. */
675
+ scrollToKey(params: {
676
+ key: string;
677
+ animated?: boolean;
678
+ viewOffset?: number;
679
+ viewPosition?: number;
680
+ }): Promise<void>;
681
+ /**
682
+ * Captures an item's key and viewport offset, holding it through layout changes by default.
683
+ * Omit key to use the first visible item; set hold to false to read without side effects.
684
+ * Returns undefined before initial placement, during dataset replacement, or if no item is found.
685
+ * While following at the end, returns the anchor without interrupting end following.
686
+ */
687
+ anchorVisibleContent(options?: {
688
+ key?: string;
689
+ hold?: boolean;
690
+ }): {
691
+ key: string;
692
+ offset: number;
693
+ } | undefined;
636
694
  /**
637
695
  * Clears internal virtualization caches.
638
696
  * @param options - Cache clearing options.
@@ -675,20 +733,40 @@ type LegendListRef$1 = {
675
733
  * @param params - Parameters for scrolling.
676
734
  * @param params.animated - If true, animates the scroll. Default: true.
677
735
  * @param params.index - The index to scroll to.
736
+ * @param params.itemOffset - Region start measured from the item's start along the scroll axis. Default: 0 when itemLength is provided.
737
+ * @param params.itemLength - Region length along the scroll axis. Default: 0 when itemOffset is provided. Omit both to reveal the whole item.
738
+ * @param params.viewportMargin - Space to keep clear at the start and end of the viewport along the scroll axis.
739
+ * The region should be within the item. A visible region does not scroll; otherwise its nearest edge aligns inside the margins. A region larger than the available space aligns its start.
678
740
  */
679
741
  scrollIndexIntoView(params: {
680
742
  animated?: boolean | undefined;
681
743
  index: number;
744
+ itemOffset?: number;
745
+ itemLength?: number;
746
+ viewportMargin?: {
747
+ start?: number;
748
+ end?: number;
749
+ };
682
750
  }): Promise<void>;
683
751
  /**
684
- * Scrolls a specific index into view.
752
+ * Scrolls a specific item into view.
685
753
  * @param params - Parameters for scrolling.
686
754
  * @param params.animated - If true, animates the scroll. Default: true.
687
755
  * @param params.item - The item to scroll to.
756
+ * @param params.itemOffset - Region start measured from the item's start along the scroll axis. Default: 0 when itemLength is provided.
757
+ * @param params.itemLength - Region length along the scroll axis. Default: 0 when itemOffset is provided. Omit both to reveal the whole item.
758
+ * @param params.viewportMargin - Space to keep clear at the start and end of the viewport along the scroll axis.
759
+ * The region should be within the item. A visible region does not scroll; otherwise its nearest edge aligns inside the margins. A region larger than the available space aligns its start.
688
760
  */
689
761
  scrollItemIntoView(params: {
690
762
  animated?: boolean | undefined;
691
763
  item: any;
764
+ itemOffset?: number;
765
+ itemLength?: number;
766
+ viewportMargin?: {
767
+ start?: number;
768
+ end?: number;
769
+ };
692
770
  }): Promise<void>;
693
771
  /**
694
772
  * Scrolls to the end of the list.
@@ -818,7 +896,9 @@ type LegendListPropsOverrides<ItemT, TItemType extends string | undefined> = Omi
818
896
  };
819
897
  type LegendListProps<ItemT = any, TItemType extends string | undefined = string | undefined> = LegendListPropsOverrides<ItemT, TItemType>;
820
898
  type LegendListRef = Omit<LegendListRef$1, "getAnimatableRef" | "getNativeScrollRef" | "getScrollResponder" | "reportContentInset"> & {
899
+ /** Call cancelScroll() before scrolling it directly so end following and list scrolling stop first. */
821
900
  getAnimatableRef(): React.ElementRef<typeof ScrollViewComponent>;
901
+ /** Call cancelScroll() before scrolling it directly so end following and list scrolling stop first. */
822
902
  getNativeScrollRef(): React.ElementRef<typeof ScrollViewComponent>;
823
903
  getScrollResponder(): ScrollResponderMixin;
824
904
  reportContentInset(inset?: Partial<Insets$1> | null): void;
@@ -875,6 +955,14 @@ type SectionListProps<ItemT, SectionT extends SectionBase<ItemT> = SectionBase<I
875
955
  stickySectionHeadersEnabled?: boolean;
876
956
  onViewableItemsChanged?: SectionListOnViewableItemsChanged<ItemT, SectionT>;
877
957
  };
958
+ /**
959
+ * Key-based methods use flattened keys: `${sectionKey}:item:${itemKey}` for items,
960
+ * `${sectionKey}:header` and `${sectionKey}:footer` for section chrome,
961
+ * `${sectionKey}:separator:${itemIndex}` for item separators, and
962
+ * `${sectionKey}:section-separator` for section separators.
963
+ * `sectionKey` is the section's key, or `section-${sectionIndex}` when omitted; `itemKey` comes
964
+ * from the section or list keyExtractor. anchorVisibleContent accepts and returns these same keys.
965
+ */
878
966
  type SectionListRef = LegendListRef & {
879
967
  scrollToLocation(params: SectionListScrollParams): void;
880
968
  };
package/.DS_Store DELETED
Binary file