@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 +10 -0
- package/index.d.mts +22 -4
- package/index.d.ts +22 -4
- package/index.js +52 -15
- package/index.mjs +52 -15
- package/keyboard.d.mts +1 -1
- package/keyboard.d.ts +1 -1
- package/keyboard.js +133 -25
- package/keyboard.mjs +136 -28
- package/package.json +1 -1
- package/reanimated.d.mts +3 -3
- package/reanimated.d.ts +3 -3
- package/reanimated.js +3 -3
- package/reanimated.mjs +2 -2
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
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 =
|
|
176
|
+
const animatedProps = reactNativeReanimated.useAnimatedProps(() => {
|
|
82
177
|
"worklet";
|
|
83
178
|
var _a, _b, _c, _d;
|
|
84
|
-
|
|
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.
|
|
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
|
-
})
|
|
93
|
-
const style =
|
|
94
|
-
|
|
95
|
-
|
|
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:
|
|
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,
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
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 =
|
|
155
|
+
const animatedProps = useAnimatedProps(() => {
|
|
61
156
|
"worklet";
|
|
62
157
|
var _a, _b, _c, _d;
|
|
63
|
-
|
|
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.
|
|
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
|
-
})
|
|
72
|
-
const style =
|
|
73
|
-
|
|
74
|
-
|
|
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:
|
|
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
package/reanimated.d.mts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ComponentProps } from 'react';
|
|
3
|
-
import
|
|
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
|
|
7
|
+
type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Reanimated.ScrollView>>;
|
|
8
8
|
interface AnimatedLegendListPropsBase<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
|
|
9
|
-
refScrollView?: React.Ref<
|
|
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
|
|
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
|
|
7
|
+
type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Reanimated.ScrollView>>;
|
|
8
8
|
interface AnimatedLegendListPropsBase<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
|
|
9
|
-
refScrollView?: React.Ref<
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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;
|