@legendapp/list 2.0.16 → 2.0.18

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/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 2.0.18
2
+ - Improvement: KeyboardAvoidingLegendList now supports KeyboardGestureArea with improved interactive behavior
3
+
4
+ ## 2.0.17
5
+ - Feat: Add stickyHeaderOffset property to control sticky header positioning
6
+ - Feat: Add sticky header backdrop component support
7
+ - Fix: Improve KeyboardAvoidingLegendList quality by using animated contentOffset y instead of reanimated scrollTo
8
+ - Fix: Initial scroll could sometimes be out of range beyond the ScrollView if some items are much larger than the estimated size
9
+ - Fix: Item layout updates now work correctly when container is the exact same size as previous item on old arch
10
+
1
11
  ## 2.0.16
2
12
  - Feat: Add KeyboardAvoidingLegendList component for better keyboard handling integration
3
13
  - Fix: Stale containers are not being removed and overlap with new data when using getItemType #335
package/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React$1 from 'react';
2
2
  import { ComponentProps, Key, ReactNode, Dispatch, SetStateAction } from 'react';
3
3
  import { View, Animated, ScrollView, LayoutRectangle, ScrollViewComponent, ScrollResponderMixin, StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, ScrollViewProps } from 'react-native';
4
- import Animated$1 from 'react-native-reanimated';
4
+ import Reanimated from 'react-native-reanimated';
5
5
 
6
6
  type ListenerType = "numContainers" | "numContainersPooled" | `containerItemKey${number}` | `containerItemData${number}` | `containerPosition${number}` | `containerColumn${number}` | `containerSticky${number}` | `containerStickyOffset${number}` | "containersDidLayout" | "extraData" | "numColumns" | "lastItemKeys" | "totalSize" | "alignItemsPaddingTop" | "stylePaddingTop" | "scrollAdjust" | "scrollAdjustUserOffset" | "headerSize" | "footerSize" | "maintainVisibleContentPosition" | "debugRawScroll" | "debugComputedScroll" | "otherAxisSize" | "snapToOffsets" | "scrollSize";
7
7
  interface StateContext {
@@ -216,7 +216,7 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
216
216
  onStartReachedThreshold?: number | null | undefined;
217
217
  /**
218
218
  * Called when the sticky header changes.
219
- */
219
+ */
220
220
  onStickyHeaderChange?: (info: {
221
221
  index: number;
222
222
  item: any;
@@ -280,11 +280,29 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
280
280
  * @default undefined
281
281
  */
282
282
  stickyIndices?: number[];
283
+ /**
284
+ * Configuration for sticky headers.
285
+ * @default undefined
286
+ */
287
+ stickyHeaderConfig?: StickyHeaderConfig;
283
288
  getItemType?: (item: ItemT, index: number) => TItemType;
284
289
  getFixedItemSize?: (index: number, item: ItemT, type: TItemType) => number | undefined;
285
290
  itemsAreEqual?: (itemPrevious: ItemT, item: ItemT, index: number, data: readonly ItemT[]) => boolean;
286
291
  }
287
- type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView> | ComponentProps<typeof Animated$1.ScrollView>, TItemType extends string | undefined = string | undefined> = BaseScrollViewProps<TScrollView> & LegendListSpecificProps<ItemT, TItemType> & (DataModeProps<ItemT, TItemType> | ChildrenModeProps);
292
+ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView> | ComponentProps<typeof Reanimated.ScrollView>, TItemType extends string | undefined = string | undefined> = BaseScrollViewProps<TScrollView> & LegendListSpecificProps<ItemT, TItemType> & (DataModeProps<ItemT, TItemType> | ChildrenModeProps);
293
+ interface StickyHeaderConfig {
294
+ /**
295
+ * Specifies how far from the top edge sticky headers should start sticking.
296
+ * Useful for scenarios with a fixed navbar or header, where sticky elements pin below it..
297
+ * @default 0
298
+ */
299
+ offset?: number;
300
+ /**
301
+ * Component to render as a backdrop behind the sticky header.
302
+ * @default undefined
303
+ */
304
+ backdropComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
305
+ }
288
306
  interface MaintainScrollAtEndOptions {
289
307
  onLayout?: boolean;
290
308
  onItemLayout?: boolean;
@@ -639,4 +657,4 @@ declare function useListScrollSize(): {
639
657
  };
640
658
  declare function useSyncLayout(): () => void;
641
659
 
642
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
660
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type StickyHeaderConfig, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
package/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React$1 from 'react';
2
2
  import { ComponentProps, Key, ReactNode, Dispatch, SetStateAction } from 'react';
3
3
  import { View, Animated, ScrollView, LayoutRectangle, ScrollViewComponent, ScrollResponderMixin, StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, ScrollViewProps } from 'react-native';
4
- import Animated$1 from 'react-native-reanimated';
4
+ import Reanimated from 'react-native-reanimated';
5
5
 
6
6
  type ListenerType = "numContainers" | "numContainersPooled" | `containerItemKey${number}` | `containerItemData${number}` | `containerPosition${number}` | `containerColumn${number}` | `containerSticky${number}` | `containerStickyOffset${number}` | "containersDidLayout" | "extraData" | "numColumns" | "lastItemKeys" | "totalSize" | "alignItemsPaddingTop" | "stylePaddingTop" | "scrollAdjust" | "scrollAdjustUserOffset" | "headerSize" | "footerSize" | "maintainVisibleContentPosition" | "debugRawScroll" | "debugComputedScroll" | "otherAxisSize" | "snapToOffsets" | "scrollSize";
7
7
  interface StateContext {
@@ -216,7 +216,7 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
216
216
  onStartReachedThreshold?: number | null | undefined;
217
217
  /**
218
218
  * Called when the sticky header changes.
219
- */
219
+ */
220
220
  onStickyHeaderChange?: (info: {
221
221
  index: number;
222
222
  item: any;
@@ -280,11 +280,29 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
280
280
  * @default undefined
281
281
  */
282
282
  stickyIndices?: number[];
283
+ /**
284
+ * Configuration for sticky headers.
285
+ * @default undefined
286
+ */
287
+ stickyHeaderConfig?: StickyHeaderConfig;
283
288
  getItemType?: (item: ItemT, index: number) => TItemType;
284
289
  getFixedItemSize?: (index: number, item: ItemT, type: TItemType) => number | undefined;
285
290
  itemsAreEqual?: (itemPrevious: ItemT, item: ItemT, index: number, data: readonly ItemT[]) => boolean;
286
291
  }
287
- type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView> | ComponentProps<typeof Animated$1.ScrollView>, TItemType extends string | undefined = string | undefined> = BaseScrollViewProps<TScrollView> & LegendListSpecificProps<ItemT, TItemType> & (DataModeProps<ItemT, TItemType> | ChildrenModeProps);
292
+ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof ScrollView> | ComponentProps<typeof Animated.ScrollView> | ComponentProps<typeof Reanimated.ScrollView>, TItemType extends string | undefined = string | undefined> = BaseScrollViewProps<TScrollView> & LegendListSpecificProps<ItemT, TItemType> & (DataModeProps<ItemT, TItemType> | ChildrenModeProps);
293
+ interface StickyHeaderConfig {
294
+ /**
295
+ * Specifies how far from the top edge sticky headers should start sticking.
296
+ * Useful for scenarios with a fixed navbar or header, where sticky elements pin below it..
297
+ * @default 0
298
+ */
299
+ offset?: number;
300
+ /**
301
+ * Component to render as a backdrop behind the sticky header.
302
+ * @default undefined
303
+ */
304
+ backdropComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
305
+ }
288
306
  interface MaintainScrollAtEndOptions {
289
307
  onLayout?: boolean;
290
308
  onItemLayout?: boolean;
@@ -639,4 +657,4 @@ declare function useListScrollSize(): {
639
657
  };
640
658
  declare function useSyncLayout(): () => void;
641
659
 
642
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
660
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type StickyHeaderConfig, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
package/index.js CHANGED
@@ -232,6 +232,15 @@ function useValue$(key, params) {
232
232
  }
233
233
  var typedForwardRef = React2.forwardRef;
234
234
  var typedMemo = React2.memo;
235
+ var getComponent = (Component) => {
236
+ if (React2__namespace.isValidElement(Component)) {
237
+ return Component;
238
+ }
239
+ if (Component) {
240
+ return /* @__PURE__ */ React2__namespace.createElement(Component, null);
241
+ }
242
+ return null;
243
+ };
235
244
 
236
245
  // src/components/PositionView.tsx
237
246
  var PositionViewState = typedMemo(function PositionView({
@@ -280,22 +289,42 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
280
289
  animatedScrollY,
281
290
  stickyOffset,
282
291
  index,
292
+ stickyHeaderConfig,
293
+ children,
283
294
  ...rest
284
295
  }) {
285
296
  const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
286
297
  const transform = React2__namespace.useMemo(() => {
298
+ var _a;
287
299
  if (animatedScrollY && stickyOffset !== void 0) {
300
+ const stickyOffset2 = (_a = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a : 0;
288
301
  const stickyPosition = animatedScrollY.interpolate({
289
302
  extrapolateLeft: "clamp",
290
303
  extrapolateRight: "extend",
291
- inputRange: [position + headerSize, position + 5e3 + headerSize],
304
+ inputRange: [position + headerSize - stickyOffset2, position + 5e3 + headerSize - stickyOffset2],
292
305
  outputRange: [position, position + 5e3]
293
306
  });
294
307
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
295
308
  }
296
- }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
309
+ }, [animatedScrollY, headerSize, horizontal, stickyOffset, position, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
297
310
  const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
298
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest });
311
+ const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
312
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
313
+ return null;
314
+ }
315
+ return /* @__PURE__ */ React2__namespace.createElement(
316
+ reactNative.View,
317
+ {
318
+ style: {
319
+ inset: 0,
320
+ pointerEvents: "none",
321
+ position: "absolute"
322
+ }
323
+ },
324
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
325
+ );
326
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
327
+ return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
299
328
  });
300
329
  var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
301
330
  var symbolFirst = Symbol();
@@ -481,7 +510,8 @@ var Container = typedMemo(function Container2({
481
510
  horizontal,
482
511
  getRenderedItem: getRenderedItem2,
483
512
  updateItemSize: updateItemSize2,
484
- ItemSeparatorComponent
513
+ ItemSeparatorComponent,
514
+ stickyHeaderConfig
485
515
  }) {
486
516
  const ctx = useStateContext();
487
517
  const { columnWrapperStyle, animatedScrollY } = ctx;
@@ -580,6 +610,7 @@ var Container = typedMemo(function Container2({
580
610
  if (!IsNewArchitecture) {
581
611
  React2.useEffect(() => {
582
612
  if (!isNullOrUndefined(itemKey)) {
613
+ didLayoutRef.current = false;
583
614
  const timeout = setTimeout(() => {
584
615
  if (!didLayoutRef.current && refLastSize.current) {
585
616
  updateItemSize2(itemKey, refLastSize.current);
@@ -603,6 +634,7 @@ var Container = typedMemo(function Container2({
603
634
  key: recycleItems ? void 0 : itemKey,
604
635
  onLayout,
605
636
  refView: ref,
637
+ stickyHeaderConfig,
606
638
  stickyOffset: isSticky ? stickyOffset : void 0,
607
639
  style
608
640
  },
@@ -617,7 +649,8 @@ var Containers = typedMemo(function Containers2({
617
649
  ItemSeparatorComponent,
618
650
  waitForInitialLayout,
619
651
  updateItemSize: updateItemSize2,
620
- getRenderedItem: getRenderedItem2
652
+ getRenderedItem: getRenderedItem2,
653
+ stickyHeaderConfig
621
654
  }) {
622
655
  const ctx = useStateContext();
623
656
  const columnWrapperStyle = ctx.columnWrapperStyle;
@@ -644,6 +677,7 @@ var Containers = typedMemo(function Containers2({
644
677
  id: i,
645
678
  key: i,
646
679
  recycleItems,
680
+ stickyHeaderConfig,
647
681
  updateItemSize: updateItemSize2
648
682
  }
649
683
  )
@@ -700,15 +734,6 @@ function SnapWrapper({ ScrollComponent, ...props }) {
700
734
  }
701
735
 
702
736
  // src/components/ListComponent.tsx
703
- var getComponent = (Component) => {
704
- if (React2__namespace.isValidElement(Component)) {
705
- return Component;
706
- }
707
- if (Component) {
708
- return /* @__PURE__ */ React2__namespace.createElement(Component, null);
709
- }
710
- return null;
711
- };
712
737
  var Padding = () => {
713
738
  const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
714
739
  return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style: { paddingTop: animPaddingTop } });
@@ -755,6 +780,7 @@ var ListComponent = typedMemo(function ListComponent2({
755
780
  onLayoutHeader,
756
781
  snapToIndices,
757
782
  stickyIndices,
783
+ stickyHeaderConfig,
758
784
  ...rest
759
785
  }) {
760
786
  const ctx = useStateContext();
@@ -800,6 +826,7 @@ var ListComponent = typedMemo(function ListComponent2({
800
826
  horizontal,
801
827
  ItemSeparatorComponent,
802
828
  recycleItems,
829
+ stickyHeaderConfig,
803
830
  updateItemSize: updateItemSize2,
804
831
  waitForInitialLayout
805
832
  }
@@ -2412,8 +2439,16 @@ function onScroll(ctx, state, event) {
2412
2439
  if (((_b = (_a = event.nativeEvent) == null ? void 0 : _a.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
2413
2440
  return;
2414
2441
  }
2415
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2442
+ let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2416
2443
  state.scrollPending = newScroll;
2444
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2445
+ if (state.initialScroll && newScroll > maxOffset) {
2446
+ newScroll = maxOffset;
2447
+ scrollTo(state, {
2448
+ noScrollingTo: true,
2449
+ offset: newScroll
2450
+ });
2451
+ }
2417
2452
  updateScroll(ctx, state, newScroll);
2418
2453
  onScrollProp == null ? void 0 : onScrollProp(event);
2419
2454
  }
@@ -2787,6 +2822,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2787
2822
  viewabilityConfig,
2788
2823
  viewabilityConfigCallbackPairs,
2789
2824
  waitForInitialLayout = true,
2825
+ stickyHeaderConfig,
2790
2826
  ...rest
2791
2827
  } = props;
2792
2828
  const { childrenMode } = rest;
@@ -3188,6 +3224,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3188
3224
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
3189
3225
  scrollEventThrottle: reactNative.Platform.OS === "web" ? 16 : void 0,
3190
3226
  snapToIndices,
3227
+ stickyHeaderConfig,
3191
3228
  stickyIndices,
3192
3229
  style,
3193
3230
  updateItemSize: fns.updateItemSize,
package/index.mjs CHANGED
@@ -211,6 +211,15 @@ function useValue$(key, params) {
211
211
  }
212
212
  var typedForwardRef = forwardRef;
213
213
  var typedMemo = memo;
214
+ var getComponent = (Component) => {
215
+ if (React2.isValidElement(Component)) {
216
+ return Component;
217
+ }
218
+ if (Component) {
219
+ return /* @__PURE__ */ React2.createElement(Component, null);
220
+ }
221
+ return null;
222
+ };
214
223
 
215
224
  // src/components/PositionView.tsx
216
225
  var PositionViewState = typedMemo(function PositionView({
@@ -259,22 +268,42 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
259
268
  animatedScrollY,
260
269
  stickyOffset,
261
270
  index,
271
+ stickyHeaderConfig,
272
+ children,
262
273
  ...rest
263
274
  }) {
264
275
  const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
265
276
  const transform = React2.useMemo(() => {
277
+ var _a;
266
278
  if (animatedScrollY && stickyOffset !== void 0) {
279
+ const stickyOffset2 = (_a = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a : 0;
267
280
  const stickyPosition = animatedScrollY.interpolate({
268
281
  extrapolateLeft: "clamp",
269
282
  extrapolateRight: "extend",
270
- inputRange: [position + headerSize, position + 5e3 + headerSize],
283
+ inputRange: [position + headerSize - stickyOffset2, position + 5e3 + headerSize - stickyOffset2],
271
284
  outputRange: [position, position + 5e3]
272
285
  });
273
286
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
274
287
  }
275
- }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
288
+ }, [animatedScrollY, headerSize, horizontal, stickyOffset, position, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
276
289
  const viewStyle = React2.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
277
- return /* @__PURE__ */ React2.createElement(Animated.View, { ref: refView, style: viewStyle, ...rest });
290
+ const renderStickyHeaderBackdrop = React2.useMemo(() => {
291
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
292
+ return null;
293
+ }
294
+ return /* @__PURE__ */ React2.createElement(
295
+ View,
296
+ {
297
+ style: {
298
+ inset: 0,
299
+ pointerEvents: "none",
300
+ position: "absolute"
301
+ }
302
+ },
303
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
304
+ );
305
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
306
+ return /* @__PURE__ */ React2.createElement(Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
278
307
  });
279
308
  var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
280
309
  var symbolFirst = Symbol();
@@ -460,7 +489,8 @@ var Container = typedMemo(function Container2({
460
489
  horizontal,
461
490
  getRenderedItem: getRenderedItem2,
462
491
  updateItemSize: updateItemSize2,
463
- ItemSeparatorComponent
492
+ ItemSeparatorComponent,
493
+ stickyHeaderConfig
464
494
  }) {
465
495
  const ctx = useStateContext();
466
496
  const { columnWrapperStyle, animatedScrollY } = ctx;
@@ -559,6 +589,7 @@ var Container = typedMemo(function Container2({
559
589
  if (!IsNewArchitecture) {
560
590
  useEffect(() => {
561
591
  if (!isNullOrUndefined(itemKey)) {
592
+ didLayoutRef.current = false;
562
593
  const timeout = setTimeout(() => {
563
594
  if (!didLayoutRef.current && refLastSize.current) {
564
595
  updateItemSize2(itemKey, refLastSize.current);
@@ -582,6 +613,7 @@ var Container = typedMemo(function Container2({
582
613
  key: recycleItems ? void 0 : itemKey,
583
614
  onLayout,
584
615
  refView: ref,
616
+ stickyHeaderConfig,
585
617
  stickyOffset: isSticky ? stickyOffset : void 0,
586
618
  style
587
619
  },
@@ -596,7 +628,8 @@ var Containers = typedMemo(function Containers2({
596
628
  ItemSeparatorComponent,
597
629
  waitForInitialLayout,
598
630
  updateItemSize: updateItemSize2,
599
- getRenderedItem: getRenderedItem2
631
+ getRenderedItem: getRenderedItem2,
632
+ stickyHeaderConfig
600
633
  }) {
601
634
  const ctx = useStateContext();
602
635
  const columnWrapperStyle = ctx.columnWrapperStyle;
@@ -623,6 +656,7 @@ var Containers = typedMemo(function Containers2({
623
656
  id: i,
624
657
  key: i,
625
658
  recycleItems,
659
+ stickyHeaderConfig,
626
660
  updateItemSize: updateItemSize2
627
661
  }
628
662
  )
@@ -679,15 +713,6 @@ function SnapWrapper({ ScrollComponent, ...props }) {
679
713
  }
680
714
 
681
715
  // src/components/ListComponent.tsx
682
- var getComponent = (Component) => {
683
- if (React2.isValidElement(Component)) {
684
- return Component;
685
- }
686
- if (Component) {
687
- return /* @__PURE__ */ React2.createElement(Component, null);
688
- }
689
- return null;
690
- };
691
716
  var Padding = () => {
692
717
  const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
693
718
  return /* @__PURE__ */ React2.createElement(Animated.View, { style: { paddingTop: animPaddingTop } });
@@ -734,6 +759,7 @@ var ListComponent = typedMemo(function ListComponent2({
734
759
  onLayoutHeader,
735
760
  snapToIndices,
736
761
  stickyIndices,
762
+ stickyHeaderConfig,
737
763
  ...rest
738
764
  }) {
739
765
  const ctx = useStateContext();
@@ -779,6 +805,7 @@ var ListComponent = typedMemo(function ListComponent2({
779
805
  horizontal,
780
806
  ItemSeparatorComponent,
781
807
  recycleItems,
808
+ stickyHeaderConfig,
782
809
  updateItemSize: updateItemSize2,
783
810
  waitForInitialLayout
784
811
  }
@@ -2391,8 +2418,16 @@ function onScroll(ctx, state, event) {
2391
2418
  if (((_b = (_a = event.nativeEvent) == null ? void 0 : _a.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
2392
2419
  return;
2393
2420
  }
2394
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2421
+ let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2395
2422
  state.scrollPending = newScroll;
2423
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2424
+ if (state.initialScroll && newScroll > maxOffset) {
2425
+ newScroll = maxOffset;
2426
+ scrollTo(state, {
2427
+ noScrollingTo: true,
2428
+ offset: newScroll
2429
+ });
2430
+ }
2396
2431
  updateScroll(ctx, state, newScroll);
2397
2432
  onScrollProp == null ? void 0 : onScrollProp(event);
2398
2433
  }
@@ -2766,6 +2801,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2766
2801
  viewabilityConfig,
2767
2802
  viewabilityConfigCallbackPairs,
2768
2803
  waitForInitialLayout = true,
2804
+ stickyHeaderConfig,
2769
2805
  ...rest
2770
2806
  } = props;
2771
2807
  const { childrenMode } = rest;
@@ -3167,6 +3203,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3167
3203
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
3168
3204
  scrollEventThrottle: Platform.OS === "web" ? 16 : void 0,
3169
3205
  snapToIndices,
3206
+ stickyHeaderConfig,
3170
3207
  stickyIndices,
3171
3208
  style,
3172
3209
  updateItemSize: fns.updateItemSize,
package/keyboard.d.mts CHANGED
@@ -6,7 +6,7 @@ import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
6
 
7
7
  declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
8
8
  onScroll?: (event: ReanimatedScrollEvent) => void;
9
- contentInset?: Insets;
9
+ contentInset?: Insets | undefined;
10
10
  safeAreaInsetBottom?: number;
11
11
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
12
12
 
package/keyboard.d.ts CHANGED
@@ -6,7 +6,7 @@ import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
6
 
7
7
  declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
8
8
  onScroll?: (event: ReanimatedScrollEvent) => void;
9
- contentInset?: Insets;
9
+ contentInset?: Insets | undefined;
10
10
  safeAreaInsetBottom?: number;
11
11
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
12
12
 
package/keyboard.js CHANGED
@@ -26,73 +26,182 @@ function _interopNamespace(e) {
26
26
 
27
27
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
28
28
 
29
+ // src/integrations/keyboard.tsx
30
+
31
+ // src/utils/helpers.ts
32
+ function isFunction(obj) {
33
+ return typeof obj === "function";
34
+ }
35
+
36
+ // src/hooks/useCombinedRef.ts
37
+ var useCombinedRef = (...refs) => {
38
+ const callback = React.useCallback((element) => {
39
+ for (const ref of refs) {
40
+ if (!ref) {
41
+ continue;
42
+ }
43
+ if (isFunction(ref)) {
44
+ ref(element);
45
+ } else {
46
+ ref.current = element;
47
+ }
48
+ }
49
+ }, refs);
50
+ return callback;
51
+ };
52
+
29
53
  // src/integrations/keyboard.tsx
30
54
  var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegendList2(props, forwardedRef) {
31
55
  const {
32
56
  contentInset: contentInsetProp,
33
57
  horizontal,
34
58
  onScroll: onScrollProp,
35
- scrollEventThrottle,
36
59
  safeAreaInsetBottom = 0,
60
+ style: styleProp,
37
61
  ...rest
38
62
  } = props;
39
- const resolvedScrollEventThrottle = scrollEventThrottle != null ? scrollEventThrottle : 16;
63
+ const styleFlattened = reactNative.StyleSheet.flatten(styleProp);
64
+ const refLegendList = React.useRef(null);
65
+ const combinedRef = useCombinedRef(forwardedRef, refLegendList);
66
+ const isIos = reactNative.Platform.OS === "ios";
67
+ const isAndroid = reactNative.Platform.OS === "android";
40
68
  const scrollViewRef = reactNativeReanimated.useAnimatedRef();
41
69
  const scrollOffsetY = reactNativeReanimated.useSharedValue(0);
70
+ const animatedOffsetY = reactNativeReanimated.useSharedValue(null);
42
71
  const scrollOffsetAtKeyboardStart = reactNativeReanimated.useSharedValue(0);
72
+ const mode = reactNativeReanimated.useSharedValue("idle");
43
73
  const keyboardInset = reactNativeReanimated.useSharedValue(0);
74
+ const keyboardHeight = reactNativeReanimated.useSharedValue(0);
75
+ const isOpening = reactNativeReanimated.useSharedValue(false);
76
+ const didInteractive = reactNativeReanimated.useSharedValue(false);
77
+ const isKeyboardOpen = reactNativeReanimated.useSharedValue(false);
44
78
  const scrollHandler = reactNativeReanimated.useAnimatedScrollHandler(
45
79
  (event) => {
46
- scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
80
+ scrollOffsetY.set(event.contentOffset[horizontal ? "x" : "y"]);
47
81
  if (onScrollProp) {
48
82
  reactNativeReanimated.runOnJS(onScrollProp)(event);
49
83
  }
50
84
  },
51
85
  [onScrollProp, horizontal]
52
86
  );
87
+ const setScrollProcessingEnabled = React.useCallback(
88
+ (enabled) => {
89
+ var _a;
90
+ (_a = refLegendList.current) == null ? void 0 : _a.setScrollProcessingEnabled(enabled);
91
+ },
92
+ [refLegendList]
93
+ );
53
94
  reactNativeKeyboardController.useKeyboardHandler(
54
95
  // biome-ignore assist/source/useSortedKeys: prefer start/move/end
55
96
  {
56
- onStart: () => {
97
+ onStart: (event) => {
57
98
  "worklet";
58
- scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
99
+ mode.set("running");
100
+ if (isKeyboardOpen.get() && event.progress === 1 && event.height > 0) {
101
+ return;
102
+ }
103
+ if (!didInteractive.get()) {
104
+ if (event.height > 0) {
105
+ keyboardHeight.set(event.height - safeAreaInsetBottom);
106
+ }
107
+ isOpening.set(event.progress > 0);
108
+ scrollOffsetAtKeyboardStart.set(scrollOffsetY.get());
109
+ animatedOffsetY.set(scrollOffsetY.get());
110
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(false);
111
+ }
112
+ },
113
+ onInteractive: (event) => {
114
+ "worklet";
115
+ if (mode.get() !== "running") {
116
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(false);
117
+ }
118
+ mode.set("running");
119
+ if (!didInteractive.get()) {
120
+ didInteractive.set(true);
121
+ }
122
+ if (isAndroid && !horizontal) {
123
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
124
+ }
59
125
  },
60
126
  onMove: (event) => {
61
127
  "worklet";
62
- const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
63
- scrollOffsetY.value = targetOffset;
64
- reactNativeReanimated.scrollTo(scrollViewRef, 0, targetOffset, false);
65
- if (!horizontal) {
66
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
128
+ if (!didInteractive.get()) {
129
+ const vIsOpening = isOpening.get();
130
+ const vKeyboardHeight = keyboardHeight.get();
131
+ const vProgress = vIsOpening ? event.progress : 1 - event.progress;
132
+ const targetOffset = Math.max(
133
+ 0,
134
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress
135
+ );
136
+ scrollOffsetY.set(targetOffset);
137
+ animatedOffsetY.set(targetOffset);
138
+ if (!horizontal) {
139
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
140
+ }
67
141
  }
68
142
  },
69
143
  onEnd: (event) => {
70
144
  "worklet";
71
- const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
72
- scrollOffsetY.value = targetOffset;
73
- reactNativeReanimated.scrollTo(scrollViewRef, 0, targetOffset, false);
74
- if (!horizontal) {
75
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
145
+ const wasInteractive = didInteractive.get();
146
+ const vMode = mode.get();
147
+ mode.set("idle");
148
+ if (vMode === "running") {
149
+ if (!wasInteractive) {
150
+ const vIsOpening = isOpening.get();
151
+ const vKeyboardHeight = keyboardHeight.get();
152
+ const targetOffset = Math.max(
153
+ 0,
154
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress)
155
+ );
156
+ scrollOffsetY.set(targetOffset);
157
+ animatedOffsetY.set(targetOffset);
158
+ }
159
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(true);
160
+ didInteractive.set(false);
161
+ isKeyboardOpen.set(event.height > 0);
162
+ if (!horizontal) {
163
+ const newInset = Math.max(0, event.height - safeAreaInsetBottom);
164
+ if (newInset > 0) {
165
+ keyboardInset.set(newInset);
166
+ } else {
167
+ keyboardInset.set(newInset);
168
+ animatedOffsetY.set(scrollOffsetY.get());
169
+ }
170
+ }
76
171
  }
77
172
  }
78
173
  },
79
174
  [scrollViewRef, safeAreaInsetBottom]
80
175
  );
81
- const animatedProps = reactNative.Platform.OS === "ios" ? reactNativeReanimated.useAnimatedProps(() => {
176
+ const animatedProps = reactNativeReanimated.useAnimatedProps(() => {
82
177
  "worklet";
83
178
  var _a, _b, _c, _d;
84
- return {
179
+ const vAnimatedOffsetY = animatedOffsetY.get();
180
+ const baseProps = {
181
+ contentOffset: vAnimatedOffsetY === null ? void 0 : {
182
+ x: 0,
183
+ y: vAnimatedOffsetY
184
+ }
185
+ };
186
+ return isIos ? Object.assign(baseProps, {
85
187
  contentInset: {
86
- bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
188
+ bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.get()),
87
189
  left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
88
190
  right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
89
191
  top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
90
192
  }
91
- };
92
- }) : void 0;
93
- const style = reactNative.Platform.OS !== "ios" ? reactNativeReanimated.useAnimatedStyle(() => ({
94
- marginBottom: keyboardInset.value
95
- })) : void 0;
193
+ }) : baseProps;
194
+ });
195
+ const style = isAndroid ? reactNativeReanimated.useAnimatedStyle(
196
+ () => {
197
+ var _a;
198
+ return {
199
+ ...styleFlattened || {},
200
+ marginBottom: (_a = keyboardInset.get()) != null ? _a : 0
201
+ };
202
+ },
203
+ [styleProp, keyboardInset]
204
+ ) : void 0;
96
205
  return /* @__PURE__ */ React__namespace.createElement(
97
206
  reanimated.AnimatedLegendList,
98
207
  {
@@ -100,9 +209,8 @@ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegen
100
209
  animatedProps,
101
210
  keyboardDismissMode: "interactive",
102
211
  onScroll: scrollHandler,
103
- ref: forwardedRef,
212
+ ref: combinedRef,
104
213
  refScrollView: scrollViewRef,
105
- scrollEventThrottle: resolvedScrollEventThrottle,
106
214
  scrollIndicatorInsets: { bottom: 0, top: 0 },
107
215
  style
108
216
  }
package/keyboard.mjs CHANGED
@@ -1,77 +1,186 @@
1
1
  import * as React from 'react';
2
- import { forwardRef } from 'react';
3
- import { Platform } from 'react-native';
2
+ import { forwardRef, useRef, useCallback } from 'react';
3
+ import { StyleSheet, Platform } from 'react-native';
4
4
  import { useKeyboardHandler } from 'react-native-keyboard-controller';
5
- import { useAnimatedRef, useSharedValue, useAnimatedScrollHandler, runOnJS, scrollTo, useAnimatedProps, useAnimatedStyle } from 'react-native-reanimated';
5
+ import { useAnimatedRef, useSharedValue, useAnimatedScrollHandler, runOnJS, useAnimatedProps, useAnimatedStyle } from 'react-native-reanimated';
6
6
  import { AnimatedLegendList } from '@legendapp/list/reanimated';
7
7
 
8
+ // src/integrations/keyboard.tsx
9
+
10
+ // src/utils/helpers.ts
11
+ function isFunction(obj) {
12
+ return typeof obj === "function";
13
+ }
14
+
15
+ // src/hooks/useCombinedRef.ts
16
+ var useCombinedRef = (...refs) => {
17
+ const callback = useCallback((element) => {
18
+ for (const ref of refs) {
19
+ if (!ref) {
20
+ continue;
21
+ }
22
+ if (isFunction(ref)) {
23
+ ref(element);
24
+ } else {
25
+ ref.current = element;
26
+ }
27
+ }
28
+ }, refs);
29
+ return callback;
30
+ };
31
+
8
32
  // src/integrations/keyboard.tsx
9
33
  var KeyboardAvoidingLegendList = forwardRef(function KeyboardAvoidingLegendList2(props, forwardedRef) {
10
34
  const {
11
35
  contentInset: contentInsetProp,
12
36
  horizontal,
13
37
  onScroll: onScrollProp,
14
- scrollEventThrottle,
15
38
  safeAreaInsetBottom = 0,
39
+ style: styleProp,
16
40
  ...rest
17
41
  } = props;
18
- const resolvedScrollEventThrottle = scrollEventThrottle != null ? scrollEventThrottle : 16;
42
+ const styleFlattened = StyleSheet.flatten(styleProp);
43
+ const refLegendList = useRef(null);
44
+ const combinedRef = useCombinedRef(forwardedRef, refLegendList);
45
+ const isIos = Platform.OS === "ios";
46
+ const isAndroid = Platform.OS === "android";
19
47
  const scrollViewRef = useAnimatedRef();
20
48
  const scrollOffsetY = useSharedValue(0);
49
+ const animatedOffsetY = useSharedValue(null);
21
50
  const scrollOffsetAtKeyboardStart = useSharedValue(0);
51
+ const mode = useSharedValue("idle");
22
52
  const keyboardInset = useSharedValue(0);
53
+ const keyboardHeight = useSharedValue(0);
54
+ const isOpening = useSharedValue(false);
55
+ const didInteractive = useSharedValue(false);
56
+ const isKeyboardOpen = useSharedValue(false);
23
57
  const scrollHandler = useAnimatedScrollHandler(
24
58
  (event) => {
25
- scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
59
+ scrollOffsetY.set(event.contentOffset[horizontal ? "x" : "y"]);
26
60
  if (onScrollProp) {
27
61
  runOnJS(onScrollProp)(event);
28
62
  }
29
63
  },
30
64
  [onScrollProp, horizontal]
31
65
  );
66
+ const setScrollProcessingEnabled = useCallback(
67
+ (enabled) => {
68
+ var _a;
69
+ (_a = refLegendList.current) == null ? void 0 : _a.setScrollProcessingEnabled(enabled);
70
+ },
71
+ [refLegendList]
72
+ );
32
73
  useKeyboardHandler(
33
74
  // biome-ignore assist/source/useSortedKeys: prefer start/move/end
34
75
  {
35
- onStart: () => {
76
+ onStart: (event) => {
77
+ "worklet";
78
+ mode.set("running");
79
+ if (isKeyboardOpen.get() && event.progress === 1 && event.height > 0) {
80
+ return;
81
+ }
82
+ if (!didInteractive.get()) {
83
+ if (event.height > 0) {
84
+ keyboardHeight.set(event.height - safeAreaInsetBottom);
85
+ }
86
+ isOpening.set(event.progress > 0);
87
+ scrollOffsetAtKeyboardStart.set(scrollOffsetY.get());
88
+ animatedOffsetY.set(scrollOffsetY.get());
89
+ runOnJS(setScrollProcessingEnabled)(false);
90
+ }
91
+ },
92
+ onInteractive: (event) => {
36
93
  "worklet";
37
- scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
94
+ if (mode.get() !== "running") {
95
+ runOnJS(setScrollProcessingEnabled)(false);
96
+ }
97
+ mode.set("running");
98
+ if (!didInteractive.get()) {
99
+ didInteractive.set(true);
100
+ }
101
+ if (isAndroid && !horizontal) {
102
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
103
+ }
38
104
  },
39
105
  onMove: (event) => {
40
106
  "worklet";
41
- const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
42
- scrollOffsetY.value = targetOffset;
43
- scrollTo(scrollViewRef, 0, targetOffset, false);
44
- if (!horizontal) {
45
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
107
+ if (!didInteractive.get()) {
108
+ const vIsOpening = isOpening.get();
109
+ const vKeyboardHeight = keyboardHeight.get();
110
+ const vProgress = vIsOpening ? event.progress : 1 - event.progress;
111
+ const targetOffset = Math.max(
112
+ 0,
113
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress
114
+ );
115
+ scrollOffsetY.set(targetOffset);
116
+ animatedOffsetY.set(targetOffset);
117
+ if (!horizontal) {
118
+ keyboardInset.set(Math.max(0, event.height - safeAreaInsetBottom));
119
+ }
46
120
  }
47
121
  },
48
122
  onEnd: (event) => {
49
123
  "worklet";
50
- const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
51
- scrollOffsetY.value = targetOffset;
52
- scrollTo(scrollViewRef, 0, targetOffset, false);
53
- if (!horizontal) {
54
- keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
124
+ const wasInteractive = didInteractive.get();
125
+ const vMode = mode.get();
126
+ mode.set("idle");
127
+ if (vMode === "running") {
128
+ if (!wasInteractive) {
129
+ const vIsOpening = isOpening.get();
130
+ const vKeyboardHeight = keyboardHeight.get();
131
+ const targetOffset = Math.max(
132
+ 0,
133
+ scrollOffsetAtKeyboardStart.get() + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress)
134
+ );
135
+ scrollOffsetY.set(targetOffset);
136
+ animatedOffsetY.set(targetOffset);
137
+ }
138
+ runOnJS(setScrollProcessingEnabled)(true);
139
+ didInteractive.set(false);
140
+ isKeyboardOpen.set(event.height > 0);
141
+ if (!horizontal) {
142
+ const newInset = Math.max(0, event.height - safeAreaInsetBottom);
143
+ if (newInset > 0) {
144
+ keyboardInset.set(newInset);
145
+ } else {
146
+ keyboardInset.set(newInset);
147
+ animatedOffsetY.set(scrollOffsetY.get());
148
+ }
149
+ }
55
150
  }
56
151
  }
57
152
  },
58
153
  [scrollViewRef, safeAreaInsetBottom]
59
154
  );
60
- const animatedProps = Platform.OS === "ios" ? useAnimatedProps(() => {
155
+ const animatedProps = useAnimatedProps(() => {
61
156
  "worklet";
62
157
  var _a, _b, _c, _d;
63
- return {
158
+ const vAnimatedOffsetY = animatedOffsetY.get();
159
+ const baseProps = {
160
+ contentOffset: vAnimatedOffsetY === null ? void 0 : {
161
+ x: 0,
162
+ y: vAnimatedOffsetY
163
+ }
164
+ };
165
+ return isIos ? Object.assign(baseProps, {
64
166
  contentInset: {
65
- bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
167
+ bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.get()),
66
168
  left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
67
169
  right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
68
170
  top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
69
171
  }
70
- };
71
- }) : void 0;
72
- const style = Platform.OS !== "ios" ? useAnimatedStyle(() => ({
73
- marginBottom: keyboardInset.value
74
- })) : void 0;
172
+ }) : baseProps;
173
+ });
174
+ const style = isAndroid ? useAnimatedStyle(
175
+ () => {
176
+ var _a;
177
+ return {
178
+ ...styleFlattened || {},
179
+ marginBottom: (_a = keyboardInset.get()) != null ? _a : 0
180
+ };
181
+ },
182
+ [styleProp, keyboardInset]
183
+ ) : void 0;
75
184
  return /* @__PURE__ */ React.createElement(
76
185
  AnimatedLegendList,
77
186
  {
@@ -79,9 +188,8 @@ var KeyboardAvoidingLegendList = forwardRef(function KeyboardAvoidingLegendList2
79
188
  animatedProps,
80
189
  keyboardDismissMode: "interactive",
81
190
  onScroll: scrollHandler,
82
- ref: forwardedRef,
191
+ ref: combinedRef,
83
192
  refScrollView: scrollViewRef,
84
- scrollEventThrottle: resolvedScrollEventThrottle,
85
193
  scrollIndicatorInsets: { bottom: 0, top: 0 },
86
194
  style
87
195
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "2.0.16",
3
+ "version": "2.0.18",
4
4
  "description": "Legend List is a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,
package/reanimated.d.mts CHANGED
@@ -1,12 +1,12 @@
1
1
  import * as React from 'react';
2
2
  import { ComponentProps } from 'react';
3
- import Animated from 'react-native-reanimated';
3
+ import Reanimated from 'react-native-reanimated';
4
4
  import { LegendListPropsBase, LegendListRef } from '@legendapp/list';
5
5
 
6
6
  type KeysToOmit = "getEstimatedItemSize" | "getFixedItemSize" | "getItemType" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged" | "itemsAreEqual" | "ItemSeparatorComponent";
7
- type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Animated.ScrollView>>;
7
+ type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Reanimated.ScrollView>>;
8
8
  interface AnimatedLegendListPropsBase<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
9
- refScrollView?: React.Ref<Animated.ScrollView>;
9
+ refScrollView?: React.Ref<Reanimated.ScrollView>;
10
10
  }
11
11
  type OtherAnimatedLegendListProps<ItemT> = Pick<PropsBase<ItemT>, KeysToOmit>;
12
12
  type AnimatedLegendListProps<ItemT> = Omit<AnimatedLegendListPropsBase<ItemT>, "refLegendList" | "ref"> & OtherAnimatedLegendListProps<ItemT>;
package/reanimated.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import * as React from 'react';
2
2
  import { ComponentProps } from 'react';
3
- import Animated from 'react-native-reanimated';
3
+ import Reanimated from 'react-native-reanimated';
4
4
  import { LegendListPropsBase, LegendListRef } from '@legendapp/list';
5
5
 
6
6
  type KeysToOmit = "getEstimatedItemSize" | "getFixedItemSize" | "getItemType" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged" | "itemsAreEqual" | "ItemSeparatorComponent";
7
- type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Animated.ScrollView>>;
7
+ type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Reanimated.ScrollView>>;
8
8
  interface AnimatedLegendListPropsBase<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
9
- refScrollView?: React.Ref<Animated.ScrollView>;
9
+ refScrollView?: React.Ref<Reanimated.ScrollView>;
10
10
  }
11
11
  type OtherAnimatedLegendListProps<ItemT> = Pick<PropsBase<ItemT>, KeysToOmit>;
12
12
  type AnimatedLegendListProps<ItemT> = Omit<AnimatedLegendListPropsBase<ItemT>, "refLegendList" | "ref"> & OtherAnimatedLegendListProps<ItemT>;
package/reanimated.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var React = require('react');
4
- var Animated = require('react-native-reanimated');
4
+ var Reanimated = require('react-native-reanimated');
5
5
  var list = require('@legendapp/list');
6
6
 
7
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -25,7 +25,7 @@ function _interopNamespace(e) {
25
25
  }
26
26
 
27
27
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
28
- var Animated__default = /*#__PURE__*/_interopDefault(Animated);
28
+ var Reanimated__default = /*#__PURE__*/_interopDefault(Reanimated);
29
29
 
30
30
  // src/integrations/reanimated.tsx
31
31
 
@@ -65,7 +65,7 @@ var LegendListForwardedRef = typedMemo(
65
65
  return /* @__PURE__ */ React__namespace.createElement(list.LegendList, { ref: refFn, refScrollView: ref, ...rest });
66
66
  })
67
67
  );
68
- var AnimatedLegendListComponent = Animated__default.default.createAnimatedComponent(LegendListForwardedRef);
68
+ var AnimatedLegendListComponent = Reanimated__default.default.createAnimatedComponent(LegendListForwardedRef);
69
69
  var AnimatedLegendList = typedMemo(
70
70
  React__namespace.forwardRef(function AnimatedLegendList2(props, ref) {
71
71
  const { refScrollView, ...rest } = props;
package/reanimated.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { useCallback, memo } from 'react';
3
- import Animated from 'react-native-reanimated';
3
+ import Reanimated from 'react-native-reanimated';
4
4
  import { LegendList } from '@legendapp/list';
5
5
 
6
6
  // src/integrations/reanimated.tsx
@@ -41,7 +41,7 @@ var LegendListForwardedRef = typedMemo(
41
41
  return /* @__PURE__ */ React.createElement(LegendList, { ref: refFn, refScrollView: ref, ...rest });
42
42
  })
43
43
  );
44
- var AnimatedLegendListComponent = Animated.createAnimatedComponent(LegendListForwardedRef);
44
+ var AnimatedLegendListComponent = Reanimated.createAnimatedComponent(LegendListForwardedRef);
45
45
  var AnimatedLegendList = typedMemo(
46
46
  React.forwardRef(function AnimatedLegendList2(props, ref) {
47
47
  const { refScrollView, ...rest } = props;