@legendapp/list 2.0.15 → 2.0.17

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,15 @@
1
+ ## 2.0.17
2
+ - Feat: Add stickyHeaderOffset property to control sticky header positioning
3
+ - Feat: Add sticky header backdrop component support
4
+ - Fix: Improve KeyboardAvoidingLegendList quality by using animated contentOffset y instead of reanimated scrollTo
5
+ - Fix: Initial scroll could sometimes be out of range beyond the ScrollView if some items are much larger than the estimated size
6
+ - Fix: Item layout updates now work correctly when container is the exact same size as previous item on old arch
7
+
8
+ ## 2.0.16
9
+ - Feat: Add KeyboardAvoidingLegendList component for better keyboard handling integration
10
+ - Fix: Stale containers are not being removed and overlap with new data when using getItemType #335
11
+ - Fix: Suppress keyExtractor warning when using lazy list mode #330
12
+
1
13
  ## 2.0.15
2
14
  - Fix: Container allocation for sticky headers could duplicate containers, causing rendering issues
3
15
  - Fix: Sticky positioned components scrolling out of viewport after scrolling distance exceeded 5000
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
  }
@@ -1572,14 +1599,14 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1572
1599
  continue;
1573
1600
  }
1574
1601
  const key = peek$(ctx, `containerItemKey${u}`);
1575
- let isOk = key === void 0;
1576
- if (!isOk && pendingRemovalSet.has(u)) {
1577
- pendingRemovalSet.delete(u);
1578
- pendingRemovalChanged = true;
1579
- const requiredType = neededTypes[typeIndex];
1580
- isOk = canReuseContainer(u, requiredType);
1581
- }
1582
- if (isOk) {
1602
+ const requiredType = neededTypes[typeIndex];
1603
+ const isPending = key !== void 0 && pendingRemovalSet.has(u);
1604
+ const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
1605
+ if (canUse) {
1606
+ if (isPending) {
1607
+ pendingRemovalSet.delete(u);
1608
+ pendingRemovalChanged = true;
1609
+ }
1583
1610
  result.push(u);
1584
1611
  if (requiredItemTypes) {
1585
1612
  typeIndex++;
@@ -2412,8 +2439,17 @@ 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
+ console.log("newScroll", newScroll);
2445
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2446
+ if (state.initialScroll && newScroll > maxOffset) {
2447
+ newScroll = maxOffset;
2448
+ scrollTo(state, {
2449
+ noScrollingTo: true,
2450
+ offset: newScroll
2451
+ });
2452
+ }
2417
2453
  updateScroll(ctx, state, newScroll);
2418
2454
  onScrollProp == null ? void 0 : onScrollProp(event);
2419
2455
  }
@@ -2722,6 +2758,7 @@ var LegendList = typedMemo(
2722
2758
  const isChildrenMode = children !== void 0 && dataProp === void 0;
2723
2759
  const processedProps = isChildrenMode ? {
2724
2760
  ...restProps,
2761
+ childrenMode: true,
2725
2762
  data: (isArray(children) ? children : React2__namespace.Children.toArray(children)).flat(1),
2726
2763
  renderItem: ({ item }) => item
2727
2764
  } : {
@@ -2786,8 +2823,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2786
2823
  viewabilityConfig,
2787
2824
  viewabilityConfigCallbackPairs,
2788
2825
  waitForInitialLayout = true,
2826
+ stickyHeaderConfig,
2789
2827
  ...rest
2790
2828
  } = props;
2829
+ const { childrenMode } = rest;
2791
2830
  const [renderNum, setRenderNum] = React2.useState(0);
2792
2831
  const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2793
2832
  const [canRender, setCanRender] = React2__namespace.useState(!IsNewArchitecture);
@@ -2960,7 +2999,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2960
2999
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
2961
3000
  refState.current.lastBatchingAction = Date.now();
2962
3001
  if (!keyExtractorProp && !isFirst && didDataChange) {
2963
- __DEV__ && warnDevOnce(
3002
+ __DEV__ && !childrenMode && warnDevOnce(
2964
3003
  "keyExtractor",
2965
3004
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
2966
3005
  );
@@ -3186,6 +3225,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3186
3225
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
3187
3226
  scrollEventThrottle: reactNative.Platform.OS === "web" ? 16 : void 0,
3188
3227
  snapToIndices,
3228
+ stickyHeaderConfig,
3189
3229
  stickyIndices,
3190
3230
  style,
3191
3231
  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
  }
@@ -1551,14 +1578,14 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1551
1578
  continue;
1552
1579
  }
1553
1580
  const key = peek$(ctx, `containerItemKey${u}`);
1554
- let isOk = key === void 0;
1555
- if (!isOk && pendingRemovalSet.has(u)) {
1556
- pendingRemovalSet.delete(u);
1557
- pendingRemovalChanged = true;
1558
- const requiredType = neededTypes[typeIndex];
1559
- isOk = canReuseContainer(u, requiredType);
1560
- }
1561
- if (isOk) {
1581
+ const requiredType = neededTypes[typeIndex];
1582
+ const isPending = key !== void 0 && pendingRemovalSet.has(u);
1583
+ const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
1584
+ if (canUse) {
1585
+ if (isPending) {
1586
+ pendingRemovalSet.delete(u);
1587
+ pendingRemovalChanged = true;
1588
+ }
1562
1589
  result.push(u);
1563
1590
  if (requiredItemTypes) {
1564
1591
  typeIndex++;
@@ -2391,8 +2418,17 @@ 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
+ console.log("newScroll", newScroll);
2424
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2425
+ if (state.initialScroll && newScroll > maxOffset) {
2426
+ newScroll = maxOffset;
2427
+ scrollTo(state, {
2428
+ noScrollingTo: true,
2429
+ offset: newScroll
2430
+ });
2431
+ }
2396
2432
  updateScroll(ctx, state, newScroll);
2397
2433
  onScrollProp == null ? void 0 : onScrollProp(event);
2398
2434
  }
@@ -2701,6 +2737,7 @@ var LegendList = typedMemo(
2701
2737
  const isChildrenMode = children !== void 0 && dataProp === void 0;
2702
2738
  const processedProps = isChildrenMode ? {
2703
2739
  ...restProps,
2740
+ childrenMode: true,
2704
2741
  data: (isArray(children) ? children : React2.Children.toArray(children)).flat(1),
2705
2742
  renderItem: ({ item }) => item
2706
2743
  } : {
@@ -2765,8 +2802,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2765
2802
  viewabilityConfig,
2766
2803
  viewabilityConfigCallbackPairs,
2767
2804
  waitForInitialLayout = true,
2805
+ stickyHeaderConfig,
2768
2806
  ...rest
2769
2807
  } = props;
2808
+ const { childrenMode } = rest;
2770
2809
  const [renderNum, setRenderNum] = useState(0);
2771
2810
  const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2772
2811
  const [canRender, setCanRender] = React2.useState(!IsNewArchitecture);
@@ -2939,7 +2978,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2939
2978
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
2940
2979
  refState.current.lastBatchingAction = Date.now();
2941
2980
  if (!keyExtractorProp && !isFirst && didDataChange) {
2942
- __DEV__ && warnDevOnce(
2981
+ __DEV__ && !childrenMode && warnDevOnce(
2943
2982
  "keyExtractor",
2944
2983
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
2945
2984
  );
@@ -3165,6 +3204,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3165
3204
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
3166
3205
  scrollEventThrottle: Platform.OS === "web" ? 16 : void 0,
3167
3206
  snapToIndices,
3207
+ stickyHeaderConfig,
3168
3208
  stickyIndices,
3169
3209
  style,
3170
3210
  updateItemSize: fns.updateItemSize,
package/keyboard.d.mts ADDED
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ import { Insets } from 'react-native';
3
+ import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/hook/commonTypes';
4
+ import { LegendListRef } from '@legendapp/list';
5
+ import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
+
7
+ declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
8
+ onScroll?: (event: ReanimatedScrollEvent) => void;
9
+ contentInset?: Insets;
10
+ safeAreaInsetBottom?: number;
11
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode;
12
+
13
+ export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
package/keyboard.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import * as React from 'react';
2
+ import { Insets } from 'react-native';
3
+ import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/hook/commonTypes';
4
+ import { LegendListRef } from '@legendapp/list';
5
+ import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
6
+
7
+ declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
8
+ onScroll?: (event: ReanimatedScrollEvent) => void;
9
+ contentInset?: Insets;
10
+ safeAreaInsetBottom?: number;
11
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode;
12
+
13
+ export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
package/keyboard.js ADDED
@@ -0,0 +1,164 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var reactNative = require('react-native');
5
+ var reactNativeKeyboardController = require('react-native-keyboard-controller');
6
+ var reactNativeReanimated = require('react-native-reanimated');
7
+ var reanimated = require('@legendapp/list/reanimated');
8
+
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
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
+
53
+ // src/integrations/keyboard.tsx
54
+ var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegendList2(props, forwardedRef) {
55
+ const {
56
+ contentInset: contentInsetProp,
57
+ horizontal,
58
+ onScroll: onScrollProp,
59
+ safeAreaInsetBottom = 0,
60
+ ...rest
61
+ } = props;
62
+ const refLegendList = React.useRef(null);
63
+ const combinedRef = useCombinedRef(forwardedRef, refLegendList);
64
+ const scrollViewRef = reactNativeReanimated.useAnimatedRef();
65
+ const scrollOffsetY = reactNativeReanimated.useSharedValue(0);
66
+ const animatedOffsetY = reactNativeReanimated.useSharedValue(null);
67
+ const scrollOffsetAtKeyboardStart = reactNativeReanimated.useSharedValue(0);
68
+ const keyboardInset = reactNativeReanimated.useSharedValue(0);
69
+ const keyboardHeight = reactNativeReanimated.useSharedValue(0);
70
+ const isOpening = reactNativeReanimated.useSharedValue(false);
71
+ const scrollHandler = reactNativeReanimated.useAnimatedScrollHandler(
72
+ (event) => {
73
+ scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
74
+ if (onScrollProp) {
75
+ reactNativeReanimated.runOnJS(onScrollProp)(event);
76
+ }
77
+ },
78
+ [onScrollProp, horizontal]
79
+ );
80
+ const setScrollProcessingEnabled = React.useCallback(
81
+ (enabled) => {
82
+ var _a;
83
+ (_a = refLegendList.current) == null ? void 0 : _a.setScrollProcessingEnabled(enabled);
84
+ },
85
+ [refLegendList]
86
+ );
87
+ reactNativeKeyboardController.useKeyboardHandler(
88
+ // biome-ignore assist/source/useSortedKeys: prefer start/move/end
89
+ {
90
+ onStart: (event) => {
91
+ "worklet";
92
+ if (event.height > 0) {
93
+ keyboardHeight.set(event.height - safeAreaInsetBottom);
94
+ }
95
+ isOpening.set(event.progress > 0);
96
+ scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
97
+ animatedOffsetY.set(scrollOffsetY.value);
98
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(false);
99
+ },
100
+ onMove: (event) => {
101
+ "worklet";
102
+ const vIsOpening = isOpening.get();
103
+ const vKeyboardHeight = keyboardHeight.get();
104
+ const vProgress = vIsOpening ? event.progress : 1 - event.progress;
105
+ const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress;
106
+ scrollOffsetY.value = targetOffset;
107
+ animatedOffsetY.set(targetOffset);
108
+ if (!horizontal) {
109
+ keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
110
+ }
111
+ },
112
+ onEnd: (event) => {
113
+ "worklet";
114
+ const vIsOpening = isOpening.get();
115
+ const vKeyboardHeight = keyboardHeight.get();
116
+ const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress);
117
+ scrollOffsetY.value = targetOffset;
118
+ animatedOffsetY.set(targetOffset);
119
+ if (!horizontal) {
120
+ keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
121
+ }
122
+ reactNativeReanimated.runOnJS(setScrollProcessingEnabled)(true);
123
+ }
124
+ },
125
+ [scrollViewRef, safeAreaInsetBottom]
126
+ );
127
+ const animatedProps = reactNativeReanimated.useAnimatedProps(() => {
128
+ "worklet";
129
+ var _a, _b, _c, _d;
130
+ const baseProps = {
131
+ contentOffset: animatedOffsetY.value === null ? void 0 : {
132
+ x: 0,
133
+ y: animatedOffsetY.value
134
+ }
135
+ };
136
+ return reactNative.Platform.OS === "ios" ? Object.assign(baseProps, {
137
+ contentInset: {
138
+ bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
139
+ left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
140
+ right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
141
+ top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
142
+ }
143
+ }) : baseProps;
144
+ });
145
+ const style = reactNative.Platform.OS !== "ios" ? reactNativeReanimated.useAnimatedStyle(() => ({
146
+ marginBottom: keyboardInset.value
147
+ })) : void 0;
148
+ return /* @__PURE__ */ React__namespace.createElement(
149
+ reanimated.AnimatedLegendList,
150
+ {
151
+ ...rest,
152
+ animatedProps,
153
+ keyboardDismissMode: "interactive",
154
+ onScroll: scrollHandler,
155
+ ref: combinedRef,
156
+ refScrollView: scrollViewRef,
157
+ scrollIndicatorInsets: { bottom: 0, top: 0 },
158
+ style
159
+ }
160
+ );
161
+ });
162
+
163
+ exports.KeyboardAvoidingLegendList = KeyboardAvoidingLegendList;
164
+ exports.LegendList = KeyboardAvoidingLegendList;
package/keyboard.mjs ADDED
@@ -0,0 +1,142 @@
1
+ import * as React from 'react';
2
+ import { forwardRef, useRef, useCallback } from 'react';
3
+ import { Platform } from 'react-native';
4
+ import { useKeyboardHandler } from 'react-native-keyboard-controller';
5
+ import { useAnimatedRef, useSharedValue, useAnimatedScrollHandler, runOnJS, useAnimatedProps, useAnimatedStyle } from 'react-native-reanimated';
6
+ import { AnimatedLegendList } from '@legendapp/list/reanimated';
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
+
32
+ // src/integrations/keyboard.tsx
33
+ var KeyboardAvoidingLegendList = forwardRef(function KeyboardAvoidingLegendList2(props, forwardedRef) {
34
+ const {
35
+ contentInset: contentInsetProp,
36
+ horizontal,
37
+ onScroll: onScrollProp,
38
+ safeAreaInsetBottom = 0,
39
+ ...rest
40
+ } = props;
41
+ const refLegendList = useRef(null);
42
+ const combinedRef = useCombinedRef(forwardedRef, refLegendList);
43
+ const scrollViewRef = useAnimatedRef();
44
+ const scrollOffsetY = useSharedValue(0);
45
+ const animatedOffsetY = useSharedValue(null);
46
+ const scrollOffsetAtKeyboardStart = useSharedValue(0);
47
+ const keyboardInset = useSharedValue(0);
48
+ const keyboardHeight = useSharedValue(0);
49
+ const isOpening = useSharedValue(false);
50
+ const scrollHandler = useAnimatedScrollHandler(
51
+ (event) => {
52
+ scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
53
+ if (onScrollProp) {
54
+ runOnJS(onScrollProp)(event);
55
+ }
56
+ },
57
+ [onScrollProp, horizontal]
58
+ );
59
+ const setScrollProcessingEnabled = useCallback(
60
+ (enabled) => {
61
+ var _a;
62
+ (_a = refLegendList.current) == null ? void 0 : _a.setScrollProcessingEnabled(enabled);
63
+ },
64
+ [refLegendList]
65
+ );
66
+ useKeyboardHandler(
67
+ // biome-ignore assist/source/useSortedKeys: prefer start/move/end
68
+ {
69
+ onStart: (event) => {
70
+ "worklet";
71
+ if (event.height > 0) {
72
+ keyboardHeight.set(event.height - safeAreaInsetBottom);
73
+ }
74
+ isOpening.set(event.progress > 0);
75
+ scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
76
+ animatedOffsetY.set(scrollOffsetY.value);
77
+ runOnJS(setScrollProcessingEnabled)(false);
78
+ },
79
+ onMove: (event) => {
80
+ "worklet";
81
+ const vIsOpening = isOpening.get();
82
+ const vKeyboardHeight = keyboardHeight.get();
83
+ const vProgress = vIsOpening ? event.progress : 1 - event.progress;
84
+ const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * vProgress;
85
+ scrollOffsetY.value = targetOffset;
86
+ animatedOffsetY.set(targetOffset);
87
+ if (!horizontal) {
88
+ keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
89
+ }
90
+ },
91
+ onEnd: (event) => {
92
+ "worklet";
93
+ const vIsOpening = isOpening.get();
94
+ const vKeyboardHeight = keyboardHeight.get();
95
+ const targetOffset = scrollOffsetAtKeyboardStart.value + (vIsOpening ? vKeyboardHeight : -vKeyboardHeight) * (vIsOpening ? event.progress : 1 - event.progress);
96
+ scrollOffsetY.value = targetOffset;
97
+ animatedOffsetY.set(targetOffset);
98
+ if (!horizontal) {
99
+ keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
100
+ }
101
+ runOnJS(setScrollProcessingEnabled)(true);
102
+ }
103
+ },
104
+ [scrollViewRef, safeAreaInsetBottom]
105
+ );
106
+ const animatedProps = useAnimatedProps(() => {
107
+ "worklet";
108
+ var _a, _b, _c, _d;
109
+ const baseProps = {
110
+ contentOffset: animatedOffsetY.value === null ? void 0 : {
111
+ x: 0,
112
+ y: animatedOffsetY.value
113
+ }
114
+ };
115
+ return Platform.OS === "ios" ? Object.assign(baseProps, {
116
+ contentInset: {
117
+ bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
118
+ left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
119
+ right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
120
+ top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
121
+ }
122
+ }) : baseProps;
123
+ });
124
+ const style = Platform.OS !== "ios" ? useAnimatedStyle(() => ({
125
+ marginBottom: keyboardInset.value
126
+ })) : void 0;
127
+ return /* @__PURE__ */ React.createElement(
128
+ AnimatedLegendList,
129
+ {
130
+ ...rest,
131
+ animatedProps,
132
+ keyboardDismissMode: "interactive",
133
+ onScroll: scrollHandler,
134
+ ref: combinedRef,
135
+ refScrollView: scrollViewRef,
136
+ scrollIndicatorInsets: { bottom: 0, top: 0 },
137
+ style
138
+ }
139
+ );
140
+ });
141
+
142
+ export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "2.0.15",
3
+ "version": "2.0.17",
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;