@swmansion/react-native-bottom-sheet 0.5.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/lib/module/BottomSheetBase.js +10 -13
  2. package/lib/module/BottomSheetBase.js.map +1 -1
  3. package/lib/module/BottomSheetContext.js.map +1 -1
  4. package/lib/module/BottomSheetFlatList.js +3 -42
  5. package/lib/module/BottomSheetFlatList.js.map +1 -1
  6. package/lib/module/BottomSheetScrollView.js +3 -43
  7. package/lib/module/BottomSheetScrollView.js.map +1 -1
  8. package/lib/module/bottomSheetScrollable.js +35 -0
  9. package/lib/module/bottomSheetScrollable.js.map +1 -0
  10. package/lib/module/index.js +1 -1
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/module/useBottomSheetPanGesture.js +33 -23
  13. package/lib/module/useBottomSheetPanGesture.js.map +1 -1
  14. package/lib/module/useBottomSheetScrollable.js +14 -17
  15. package/lib/module/useBottomSheetScrollable.js.map +1 -1
  16. package/lib/typescript/src/BottomSheetBase.d.ts.map +1 -1
  17. package/lib/typescript/src/BottomSheetContext.d.ts +6 -4
  18. package/lib/typescript/src/BottomSheetContext.d.ts.map +1 -1
  19. package/lib/typescript/src/BottomSheetFlatList.d.ts +7 -12
  20. package/lib/typescript/src/BottomSheetFlatList.d.ts.map +1 -1
  21. package/lib/typescript/src/BottomSheetScrollView.d.ts +7 -14
  22. package/lib/typescript/src/BottomSheetScrollView.d.ts.map +1 -1
  23. package/lib/typescript/src/bottomSheetScrollable.d.ts +9 -0
  24. package/lib/typescript/src/bottomSheetScrollable.d.ts.map +1 -0
  25. package/lib/typescript/src/index.d.ts +3 -3
  26. package/lib/typescript/src/index.d.ts.map +1 -1
  27. package/lib/typescript/src/useBottomSheetPanGesture.d.ts +4 -6
  28. package/lib/typescript/src/useBottomSheetPanGesture.d.ts.map +1 -1
  29. package/lib/typescript/src/useBottomSheetScrollable.d.ts +1 -1
  30. package/lib/typescript/src/useBottomSheetScrollable.d.ts.map +1 -1
  31. package/package.json +2 -1
  32. package/src/BottomSheetBase.tsx +16 -14
  33. package/src/BottomSheetContext.tsx +7 -4
  34. package/src/BottomSheetFlatList.tsx +16 -58
  35. package/src/BottomSheetScrollView.tsx +17 -61
  36. package/src/bottomSheetScrollable.tsx +42 -0
  37. package/src/index.tsx +3 -9
  38. package/src/useBottomSheetPanGesture.ts +44 -53
  39. package/src/useBottomSheetScrollable.ts +16 -23
@@ -1,17 +1,10 @@
1
- import { type Ref } from 'react';
2
- import type { NativeScrollEvent, ScrollViewProps } from 'react-native';
3
- type ScrollToOptions = {
4
- x?: number;
5
- y?: number;
6
- animated?: boolean;
7
- };
8
- export interface BottomSheetScrollViewProps extends Omit<ScrollViewProps, 'onScroll' | 'ref'> {
1
+ import type { Ref, ReactElement } from 'react';
2
+ import { ScrollView, type NativeScrollEvent, type ScrollViewProps } from 'react-native';
3
+ import type { SharedValue } from 'react-native-reanimated';
4
+ export type BottomSheetScrollViewProps = Omit<ScrollViewProps, 'onScroll' | 'scrollEnabled' | 'ref'> & {
5
+ scrollEnabled?: boolean | SharedValue<boolean | undefined>;
9
6
  onScroll?: (event: NativeScrollEvent) => void;
10
- ref?: Ref<BottomSheetScrollViewMethods>;
11
- }
12
- export declare const BottomSheetScrollView: ({ scrollEnabled, onScroll, ref, ...rest }: BottomSheetScrollViewProps) => import("react/jsx-runtime").JSX.Element;
13
- export type BottomSheetScrollViewMethods = {
14
- scrollTo: (options: ScrollToOptions) => void;
7
+ ref?: Ref<ScrollView>;
15
8
  };
16
- export {};
9
+ export declare const BottomSheetScrollView: (props: BottomSheetScrollViewProps) => ReactElement;
17
10
  //# sourceMappingURL=BottomSheetScrollView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BottomSheetScrollView.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetScrollView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAOvE,KAAK,eAAe,GAAG;IAAE,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,WAAW,0BACf,SAAQ,IAAI,CAAC,eAAe,EAAE,UAAU,GAAG,KAAK,CAAC;IACjD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9C,GAAG,CAAC,EAAE,GAAG,CAAC,4BAA4B,CAAC,CAAC;CACzC;AAED,eAAO,MAAM,qBAAqB,GAAI,2CAKnC,0BAA0B,4CAwC5B,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;CAC9C,CAAC"}
1
+ {"version":3,"file":"BottomSheetScrollView.d.ts","sourceRoot":"","sources":["../../../src/BottomSheetScrollView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACrB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAI3D,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC3C,eAAe,EACf,UAAU,GAAG,eAAe,GAAG,KAAK,CACrC,GAAG;IACF,aAAa,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC3D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9C,GAAG,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAwC,CACxE,KAAK,EAAE,0BAA0B,KAC9B,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type ComponentType, type Ref } from 'react';
2
+ import type { NativeScrollEvent } from 'react-native';
3
+ import { type SharedValue } from 'react-native-reanimated';
4
+ export declare function bottomSheetScrollable<P extends Record<string, any>, R = unknown>(ScrollableComponent: ComponentType<P>): ({ scrollEnabled, onScroll, ref, ...rest }: Omit<P, "onScroll" | "scrollEnabled" | "ref"> & {
5
+ scrollEnabled?: boolean | SharedValue<boolean | undefined>;
6
+ onScroll?: (event: NativeScrollEvent) => void;
7
+ ref?: Ref<R>;
8
+ }) => import("react/jsx-runtime").JSX.Element;
9
+ //# sourceMappingURL=bottomSheetScrollable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottomSheetScrollable.d.ts","sourceRoot":"","sources":["../../../src/bottomSheetScrollable.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,GAAG,EAAuB,MAAM,OAAO,CAAC;AAC1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAIrE,wBAAgB,qBAAqB,CACnC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC7B,CAAC,GAAG,OAAO,EACX,mBAAmB,EAAE,aAAa,CAAC,CAAC,CAAC,IAI7B,2CAKL,IAAI,CAAC,CAAC,EAAE,UAAU,GAAG,eAAe,GAAG,KAAK,CAAC,GAAG;IACjD,aAAa,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IAC3D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9C,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;CACd,6CAkBF"}
@@ -4,10 +4,10 @@ export { ModalBottomSheet } from './ModalBottomSheet';
4
4
  export type { ModalBottomSheetProps } from './ModalBottomSheet';
5
5
  export { BottomSheetProvider } from './BottomSheetProvider';
6
6
  export { BottomSheetFlatList } from './BottomSheetFlatList';
7
- export type { BottomSheetFlatListMethods, BottomSheetFlatListProps, } from './BottomSheetFlatList';
7
+ export type { BottomSheetFlatListProps } from './BottomSheetFlatList';
8
8
  export { BottomSheetScrollView } from './BottomSheetScrollView';
9
- export type { BottomSheetScrollViewMethods, BottomSheetScrollViewProps, } from './BottomSheetScrollView';
9
+ export type { BottomSheetScrollViewProps } from './BottomSheetScrollView';
10
10
  export type { Detent, DetentValue } from './BottomSheetBase';
11
11
  export { programmatic } from './BottomSheetBase';
12
- export { useBottomSheetScrollable } from './useBottomSheetScrollable';
12
+ export { bottomSheetScrollable } from './bottomSheetScrollable';
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EACV,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EACV,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import type { PanGesture } from 'react-native-gesture-handler';
2
- import { type AnimatedRef, type SharedValue } from 'react-native-reanimated';
2
+ import { type SharedValue } from 'react-native-reanimated';
3
+ import type { ScrollableEntry } from './BottomSheetContext';
3
4
  interface BottomSheetPanGestureParams {
4
5
  animationTarget: SharedValue<number>;
5
6
  translateY: SharedValue<number>;
@@ -7,14 +8,11 @@ interface BottomSheetPanGestureParams {
7
8
  detentsValue: SharedValue<number[]>;
8
9
  isDraggableValue: SharedValue<boolean[]>;
9
10
  currentIndex: SharedValue<number>;
10
- scrollOffset: SharedValue<number>;
11
- hasScrollable: SharedValue<boolean>;
12
- isScrollableGestureActive: SharedValue<boolean>;
11
+ scrollableEntries: ScrollableEntry[];
13
12
  isScrollableLocked: SharedValue<boolean>;
14
- scrollableRef: AnimatedRef<any>;
15
13
  handleIndexChange: (nextIndex: number) => void;
16
14
  animateToIndex: (targetIndex: number, velocity?: number) => void;
17
15
  }
18
- export declare const useBottomSheetPanGesture: ({ animationTarget, translateY, sheetHeight, detentsValue, isDraggableValue, currentIndex, scrollOffset, hasScrollable, isScrollableGestureActive, isScrollableLocked, scrollableRef, handleIndexChange, animateToIndex, }: BottomSheetPanGestureParams) => PanGesture;
16
+ export declare const useBottomSheetPanGesture: ({ animationTarget, translateY, sheetHeight, detentsValue, isDraggableValue, currentIndex, scrollableEntries, isScrollableLocked, handleIndexChange, animateToIndex, }: BottomSheetPanGestureParams) => PanGesture;
19
17
  export {};
20
18
  //# sourceMappingURL=useBottomSheetPanGesture.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useBottomSheetPanGesture.d.ts","sourceRoot":"","sources":["../../../src/useBottomSheetPanGesture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAG/D,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,yBAAyB,CAAC;AAIjC,UAAU,2BAA2B;IACnC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IACpC,gBAAgB,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,aAAa,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACpC,yBAAyB,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAChD,kBAAkB,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,aAAa,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAClE;AAED,eAAO,MAAM,wBAAwB,GAAI,2NActC,2BAA2B,KAAG,UAgMhC,CAAC"}
1
+ {"version":3,"file":"useBottomSheetPanGesture.d.ts","sourceRoot":"","sources":["../../../src/useBottomSheetPanGesture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAG/D,OAAO,EAGL,KAAK,WAAW,EAEjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAG5D,UAAU,2BAA2B;IACnC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;IACpC,gBAAgB,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACzC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,iBAAiB,EAAE,eAAe,EAAE,CAAC;IACrC,kBAAkB,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACzC,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAClE;AAED,eAAO,MAAM,wBAAwB,GAAI,uKAWtC,2BAA2B,KAAG,UA6LhC,CAAC"}
@@ -3,7 +3,7 @@ import { type SharedValue } from 'react-native-reanimated';
3
3
  type ScrollHandler = (event: NativeScrollEvent) => void;
4
4
  export declare const useBottomSheetScrollable: (baseScrollEnabled?: boolean | SharedValue<boolean | undefined>, onScroll?: ScrollHandler) => {
5
5
  scrollHandler: import("react-native-reanimated").ScrollHandlerProcessed<Record<string, unknown>>;
6
- scrollableRef: import("react-native-reanimated").AnimatedRef<any>;
6
+ scrollableRef: import("react-native-reanimated").AnimatedRef<import("react").Component<{}, {}, any>>;
7
7
  nativeGesture: import("react-native-gesture-handler/lib/typescript/handlers/gestures/nativeGesture").NativeGesture;
8
8
  animatedProps: Partial<{
9
9
  scrollEnabled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"useBottomSheetScrollable.d.ts","sourceRoot":"","sources":["../../../src/useBottomSheetScrollable.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EACL,KAAK,WAAW,EAGjB,MAAM,yBAAyB,CAAC;AAIjC,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAExD,eAAO,MAAM,wBAAwB,GACnC,oBAAmB,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,SAAS,CAAQ,EACpE,WAAW,aAAa;;;;;;;CA0DzB,CAAC"}
1
+ {"version":3,"file":"useBottomSheetScrollable.d.ts","sourceRoot":"","sources":["../../../src/useBottomSheetScrollable.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EACL,KAAK,WAAW,EAKjB,MAAM,yBAAyB,CAAC;AAIjC,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAExD,eAAO,MAAM,wBAAwB,GACnC,oBAAmB,OAAO,GAAG,WAAW,CAAC,OAAO,GAAG,SAAS,CAAQ,EACpE,WAAW,aAAa;;;;;;;CAiDzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swmansion/react-native-bottom-sheet",
3
- "version": "0.5.5",
3
+ "version": "0.6.0",
4
4
  "description": "Provides bottom-sheet components for React Native.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -63,6 +63,7 @@
63
63
  "@react-native/babel-preset": "0.83.0",
64
64
  "@react-native/eslint-config": "0.83.0",
65
65
  "@types/react": "19.2.14",
66
+ "babel-plugin-react-compiler": "1.0.0",
66
67
  "del-cli": "6.0.0",
67
68
  "eslint": "9.39.2",
68
69
  "eslint-config-prettier": "10.1.8",
@@ -4,7 +4,6 @@ import type { LayoutChangeEvent } from 'react-native';
4
4
  import { Pressable, StyleSheet, View, useWindowDimensions } from 'react-native';
5
5
  import type { SharedValue, WithSpringConfig } from 'react-native-reanimated';
6
6
  import Animated, {
7
- useAnimatedRef,
8
7
  useAnimatedReaction,
9
8
  useAnimatedStyle,
10
9
  useDerivedValue,
@@ -15,7 +14,10 @@ import { scheduleOnUI } from 'react-native-worklets';
15
14
  import { GestureDetector } from 'react-native-gesture-handler';
16
15
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
17
16
  import { Portal } from './BottomSheetProvider';
18
- import { BottomSheetContextProvider } from './BottomSheetContext';
17
+ import {
18
+ BottomSheetContextProvider,
19
+ type ScrollableEntry,
20
+ } from './BottomSheetContext';
19
21
  import {
20
22
  clampIndex,
21
23
  isDetentProgrammatic,
@@ -97,11 +99,17 @@ export const BottomSheetBase = ({
97
99
  const translateY = useSharedValue(initialMaxSnap);
98
100
  const animationTarget = useSharedValue(NaN);
99
101
  const sheetHeight = useSharedValue(initialMaxSnap);
100
- const scrollOffset = useSharedValue(0);
101
- const hasScrollable = useSharedValue(false);
102
- const isScrollableGestureActive = useSharedValue(false);
102
+ const [scrollableEntries, setScrollableEntries] = useState<ScrollableEntry[]>(
103
+ []
104
+ );
103
105
  const isScrollableLocked = useSharedValue(false);
104
- const scrollableRef = useAnimatedRef();
106
+
107
+ const registerScrollable = useCallback((entry: ScrollableEntry) => {
108
+ setScrollableEntries((prev) => [...prev, entry]);
109
+ return () => {
110
+ setScrollableEntries((prev) => prev.filter((e) => e !== entry));
111
+ };
112
+ }, []);
105
113
 
106
114
  const detentsValue = useSharedValue(normalizedDetents);
107
115
  const isDraggableValue = useSharedValue(isDraggable);
@@ -185,11 +193,8 @@ export const BottomSheetBase = ({
185
193
  detentsValue,
186
194
  isDraggableValue,
187
195
  currentIndex,
188
- scrollOffset,
189
- hasScrollable,
190
- isScrollableGestureActive,
196
+ scrollableEntries,
191
197
  isScrollableLocked,
192
- scrollableRef,
193
198
  handleIndexChange,
194
199
  animateToIndex,
195
200
  });
@@ -225,11 +230,8 @@ export const BottomSheetBase = ({
225
230
  position: internalPosition,
226
231
  index: currentIndex,
227
232
  sheetHeight,
228
- scrollOffset,
229
- scrollableRef,
230
- hasScrollable,
231
- isScrollableGestureActive,
232
233
  isScrollableLocked,
234
+ registerScrollable,
233
235
  panGesture,
234
236
  }}
235
237
  >
@@ -2,16 +2,19 @@ import { createContext, useContext } from 'react';
2
2
  import type { PanGesture } from 'react-native-gesture-handler';
3
3
  import type { AnimatedRef, SharedValue } from 'react-native-reanimated';
4
4
 
5
+ export interface ScrollableEntry {
6
+ ref: AnimatedRef<any>;
7
+ scrollOffset: SharedValue<number>;
8
+ isGestureActive: SharedValue<boolean>;
9
+ }
10
+
5
11
  export interface BottomSheetContextType {
6
12
  translateY: SharedValue<number>;
7
13
  position: SharedValue<number>;
8
14
  index: SharedValue<number>;
9
15
  sheetHeight: SharedValue<number>;
10
- scrollOffset: SharedValue<number>;
11
- scrollableRef: AnimatedRef<any>;
12
- hasScrollable: SharedValue<boolean>;
13
- isScrollableGestureActive: SharedValue<boolean>;
14
16
  isScrollableLocked: SharedValue<boolean>;
17
+ registerScrollable: (entry: ScrollableEntry) => () => void;
15
18
  panGesture: PanGesture;
16
19
  }
17
20
 
@@ -1,63 +1,21 @@
1
- import { useImperativeHandle, type Ref } from 'react';
2
- import type { NativeScrollEvent } from 'react-native';
3
- import { GestureDetector } from 'react-native-gesture-handler';
4
- import type { FlatListPropsWithLayout } from 'react-native-reanimated';
5
- import Animated, { scrollTo } from 'react-native-reanimated';
6
- import { scheduleOnUI } from 'react-native-worklets';
1
+ import { FlatList, type NativeScrollEvent } from 'react-native';
2
+ import type {
3
+ FlatListPropsWithLayout,
4
+ SharedValue,
5
+ } from 'react-native-reanimated';
6
+ import type { Ref, ReactElement } from 'react';
7
7
 
8
- import { useBottomSheetScrollable } from './useBottomSheetScrollable';
8
+ import { bottomSheetScrollable } from './bottomSheetScrollable';
9
9
 
10
- export interface BottomSheetFlatListProps<T>
11
- extends Omit<FlatListPropsWithLayout<T>, 'onScroll' | 'ref'> {
10
+ export type BottomSheetFlatListProps<T> = Omit<
11
+ FlatListPropsWithLayout<T>,
12
+ 'onScroll' | 'scrollEnabled' | 'ref'
13
+ > & {
14
+ scrollEnabled?: boolean | SharedValue<boolean | undefined>;
12
15
  onScroll?: (event: NativeScrollEvent) => void;
13
- ref?: Ref<BottomSheetFlatListMethods>;
14
- }
15
-
16
- export const BottomSheetFlatList = <T,>({
17
- scrollEnabled,
18
- onScroll,
19
- ref,
20
- ...rest
21
- }: BottomSheetFlatListProps<T>) => {
22
- const { scrollHandler, scrollableRef, nativeGesture, animatedProps } =
23
- useBottomSheetScrollable(scrollEnabled, onScroll);
24
-
25
- useImperativeHandle(
26
- ref,
27
- () => ({
28
- scrollToOffset: ({ offset, animated }) => {
29
- const resolvedAnimated = animated ?? true;
30
- scheduleOnUI(
31
- (
32
- animatedRef: Parameters<typeof scrollTo>[0],
33
- y: number,
34
- shouldAnimate: boolean
35
- ) => {
36
- 'worklet';
37
- scrollTo(animatedRef, 0, y, shouldAnimate);
38
- },
39
- scrollableRef,
40
- offset,
41
- resolvedAnimated
42
- );
43
- },
44
- }),
45
- [scrollableRef]
46
- );
47
-
48
- return (
49
- <GestureDetector gesture={nativeGesture}>
50
- <Animated.FlatList
51
- {...rest}
52
- animatedProps={animatedProps}
53
- ref={scrollableRef}
54
- onScroll={scrollHandler}
55
- scrollEventThrottle={16}
56
- />
57
- </GestureDetector>
58
- );
16
+ ref?: Ref<FlatList<T>>;
59
17
  };
60
18
 
61
- export type BottomSheetFlatListMethods = {
62
- scrollToOffset: (params: { offset: number; animated?: boolean }) => void;
63
- };
19
+ export const BottomSheetFlatList = bottomSheetScrollable(FlatList) as <T>(
20
+ props: BottomSheetFlatListProps<T>
21
+ ) => ReactElement;
@@ -1,66 +1,22 @@
1
- import { useImperativeHandle, type Ref } from 'react';
2
- import type { NativeScrollEvent, ScrollViewProps } from 'react-native';
3
- import { GestureDetector } from 'react-native-gesture-handler';
4
- import Animated, { scrollTo } from 'react-native-reanimated';
5
- import { scheduleOnUI } from 'react-native-worklets';
1
+ import type { Ref, ReactElement } from 'react';
2
+ import {
3
+ ScrollView,
4
+ type NativeScrollEvent,
5
+ type ScrollViewProps,
6
+ } from 'react-native';
7
+ import type { SharedValue } from 'react-native-reanimated';
6
8
 
7
- import { useBottomSheetScrollable } from './useBottomSheetScrollable';
9
+ import { bottomSheetScrollable } from './bottomSheetScrollable';
8
10
 
9
- type ScrollToOptions = { x?: number; y?: number; animated?: boolean };
10
-
11
- export interface BottomSheetScrollViewProps
12
- extends Omit<ScrollViewProps, 'onScroll' | 'ref'> {
11
+ export type BottomSheetScrollViewProps = Omit<
12
+ ScrollViewProps,
13
+ 'onScroll' | 'scrollEnabled' | 'ref'
14
+ > & {
15
+ scrollEnabled?: boolean | SharedValue<boolean | undefined>;
13
16
  onScroll?: (event: NativeScrollEvent) => void;
14
- ref?: Ref<BottomSheetScrollViewMethods>;
15
- }
16
-
17
- export const BottomSheetScrollView = ({
18
- scrollEnabled,
19
- onScroll,
20
- ref,
21
- ...rest
22
- }: BottomSheetScrollViewProps) => {
23
- const { scrollHandler, scrollableRef, nativeGesture, animatedProps } =
24
- useBottomSheetScrollable(scrollEnabled, onScroll);
25
-
26
- useImperativeHandle(
27
- ref,
28
- () => ({
29
- scrollTo: ({ x = 0, y = 0, animated }) => {
30
- const resolvedAnimated = animated ?? true;
31
- scheduleOnUI(
32
- (
33
- animatedRef: Parameters<typeof scrollTo>[0],
34
- xValue: number,
35
- yValue: number,
36
- shouldAnimate: boolean
37
- ) => {
38
- 'worklet';
39
- scrollTo(animatedRef, xValue, yValue, shouldAnimate);
40
- },
41
- scrollableRef,
42
- x,
43
- y,
44
- resolvedAnimated
45
- );
46
- },
47
- }),
48
- [scrollableRef]
49
- );
50
-
51
- return (
52
- <GestureDetector gesture={nativeGesture}>
53
- <Animated.ScrollView
54
- {...rest}
55
- animatedProps={animatedProps}
56
- ref={scrollableRef}
57
- onScroll={scrollHandler}
58
- scrollEventThrottle={16}
59
- />
60
- </GestureDetector>
61
- );
17
+ ref?: Ref<ScrollView>;
62
18
  };
63
19
 
64
- export type BottomSheetScrollViewMethods = {
65
- scrollTo: (options: ScrollToOptions) => void;
66
- };
20
+ export const BottomSheetScrollView = bottomSheetScrollable(ScrollView) as (
21
+ props: BottomSheetScrollViewProps
22
+ ) => ReactElement;
@@ -0,0 +1,42 @@
1
+ import { type ComponentType, type Ref, useImperativeHandle } from 'react';
2
+ import type { NativeScrollEvent } from 'react-native';
3
+ import { GestureDetector } from 'react-native-gesture-handler';
4
+ import Animated, { type SharedValue } from 'react-native-reanimated';
5
+
6
+ import { useBottomSheetScrollable } from './useBottomSheetScrollable';
7
+
8
+ export function bottomSheetScrollable<
9
+ P extends Record<string, any>,
10
+ R = unknown
11
+ >(ScrollableComponent: ComponentType<P>) {
12
+ const AnimatedComponent =
13
+ Animated.createAnimatedComponent(ScrollableComponent);
14
+
15
+ return ({
16
+ scrollEnabled,
17
+ onScroll,
18
+ ref,
19
+ ...rest
20
+ }: Omit<P, 'onScroll' | 'scrollEnabled' | 'ref'> & {
21
+ scrollEnabled?: boolean | SharedValue<boolean | undefined>;
22
+ onScroll?: (event: NativeScrollEvent) => void;
23
+ ref?: Ref<R>;
24
+ }) => {
25
+ const { scrollHandler, scrollableRef, nativeGesture, animatedProps } =
26
+ useBottomSheetScrollable(scrollEnabled, onScroll);
27
+
28
+ useImperativeHandle(ref, () => scrollableRef.current as R, [scrollableRef]);
29
+
30
+ return (
31
+ <GestureDetector gesture={nativeGesture}>
32
+ <AnimatedComponent
33
+ {...(rest as any)}
34
+ animatedProps={animatedProps}
35
+ ref={scrollableRef}
36
+ onScroll={scrollHandler}
37
+ scrollEventThrottle={16}
38
+ />
39
+ </GestureDetector>
40
+ );
41
+ };
42
+ }
package/src/index.tsx CHANGED
@@ -4,15 +4,9 @@ export { ModalBottomSheet } from './ModalBottomSheet';
4
4
  export type { ModalBottomSheetProps } from './ModalBottomSheet';
5
5
  export { BottomSheetProvider } from './BottomSheetProvider';
6
6
  export { BottomSheetFlatList } from './BottomSheetFlatList';
7
- export type {
8
- BottomSheetFlatListMethods,
9
- BottomSheetFlatListProps,
10
- } from './BottomSheetFlatList';
7
+ export type { BottomSheetFlatListProps } from './BottomSheetFlatList';
11
8
  export { BottomSheetScrollView } from './BottomSheetScrollView';
12
- export type {
13
- BottomSheetScrollViewMethods,
14
- BottomSheetScrollViewProps,
15
- } from './BottomSheetScrollView';
9
+ export type { BottomSheetScrollViewProps } from './BottomSheetScrollView';
16
10
  export type { Detent, DetentValue } from './BottomSheetBase';
17
11
  export { programmatic } from './BottomSheetBase';
18
- export { useBottomSheetScrollable } from './useBottomSheetScrollable';
12
+ export { bottomSheetScrollable } from './bottomSheetScrollable';
@@ -4,11 +4,11 @@ import { scheduleOnRN } from 'react-native-worklets';
4
4
  import {
5
5
  measure,
6
6
  scrollTo,
7
- type AnimatedRef,
8
7
  type SharedValue,
9
8
  useSharedValue,
10
9
  } from 'react-native-reanimated';
11
10
 
11
+ import type { ScrollableEntry } from './BottomSheetContext';
12
12
  import { findSnapTarget } from './bottomSheetUtils';
13
13
 
14
14
  interface BottomSheetPanGestureParams {
@@ -18,11 +18,8 @@ interface BottomSheetPanGestureParams {
18
18
  detentsValue: SharedValue<number[]>;
19
19
  isDraggableValue: SharedValue<boolean[]>;
20
20
  currentIndex: SharedValue<number>;
21
- scrollOffset: SharedValue<number>;
22
- hasScrollable: SharedValue<boolean>;
23
- isScrollableGestureActive: SharedValue<boolean>;
21
+ scrollableEntries: ScrollableEntry[];
24
22
  isScrollableLocked: SharedValue<boolean>;
25
- scrollableRef: AnimatedRef<any>;
26
23
  handleIndexChange: (nextIndex: number) => void;
27
24
  animateToIndex: (targetIndex: number, velocity?: number) => void;
28
25
  }
@@ -34,11 +31,8 @@ export const useBottomSheetPanGesture = ({
34
31
  detentsValue,
35
32
  isDraggableValue,
36
33
  currentIndex,
37
- scrollOffset,
38
- hasScrollable,
39
- isScrollableGestureActive,
34
+ scrollableEntries,
40
35
  isScrollableLocked,
41
- scrollableRef,
42
36
  handleIndexChange,
43
37
  animateToIndex,
44
38
  }: BottomSheetPanGestureParams): PanGesture => {
@@ -48,7 +42,7 @@ export const useBottomSheetPanGesture = ({
48
42
  const panStartY = useSharedValue(0);
49
43
  const panActivated = useSharedValue(false);
50
44
  const dragStartTranslateY = useSharedValue(0);
51
- const isTouchWithinScrollable = useSharedValue(false);
45
+ const activeScrollableIndex = useSharedValue(-1);
52
46
 
53
47
  return Gesture.Pan()
54
48
  .manualActivation(true)
@@ -58,21 +52,26 @@ export const useBottomSheetPanGesture = ({
58
52
  isDraggingSheet.set(false);
59
53
  isDraggingFromScrollable.set(false);
60
54
  isScrollableLocked.set(false);
61
- isTouchWithinScrollable.set(false);
55
+ activeScrollableIndex.set(-1);
62
56
  const touch = event.changedTouches[0] ?? event.allTouches[0];
63
57
  if (touch !== undefined) {
64
58
  panStartX.set(touch.absoluteX);
65
59
  panStartY.set(touch.absoluteY);
66
- if (hasScrollable.value) {
67
- const layout = measure(scrollableRef);
68
- if (layout !== null) {
69
- const withinX =
70
- touch.absoluteX >= layout.pageX &&
71
- touch.absoluteX <= layout.pageX + layout.width;
72
- const withinY =
73
- touch.absoluteY >= layout.pageY &&
74
- touch.absoluteY <= layout.pageY + layout.height;
75
- isTouchWithinScrollable.set(withinX && withinY);
60
+ const entries = scrollableEntries;
61
+ for (let i = 0; i < entries.length; i++) {
62
+ const entry = entries[i];
63
+ if (entry === undefined) continue;
64
+ const layout = measure(entry.ref);
65
+ if (layout === null) continue;
66
+ const withinX =
67
+ touch.absoluteX >= layout.pageX &&
68
+ touch.absoluteX <= layout.pageX + layout.width;
69
+ const withinY =
70
+ touch.absoluteY >= layout.pageY &&
71
+ touch.absoluteY <= layout.pageY + layout.height;
72
+ if (withinX && withinY) {
73
+ activeScrollableIndex.set(i);
74
+ break;
76
75
  }
77
76
  }
78
77
  }
@@ -84,13 +83,16 @@ export const useBottomSheetPanGesture = ({
84
83
  if (!touch) return;
85
84
  const deltaX = touch.absoluteX - panStartX.value;
86
85
  const deltaY = touch.absoluteY - panStartY.value;
87
- if (
88
- hasScrollable.value &&
89
- scrollOffset.value > 0 &&
90
- isTouchWithinScrollable.value &&
91
- translateY.value <= 0
92
- ) {
93
- return;
86
+ const activeIdx = activeScrollableIndex.value;
87
+ if (activeIdx !== -1) {
88
+ const active = scrollableEntries[activeIdx];
89
+ if (
90
+ active !== undefined &&
91
+ active.scrollOffset.value > 0 &&
92
+ translateY.value <= 0
93
+ ) {
94
+ return;
95
+ }
94
96
  }
95
97
  if (Math.abs(deltaX) > Math.abs(deltaY)) {
96
98
  stateManager.fail();
@@ -113,36 +115,30 @@ export const useBottomSheetPanGesture = ({
113
115
  })
114
116
  .onUpdate((event) => {
115
117
  'worklet';
118
+ const activeIdx = activeScrollableIndex.value;
119
+ const hasActive = activeIdx !== -1;
120
+ const active = hasActive ? scrollableEntries[activeIdx] : undefined;
121
+ const activeOffset = active !== undefined ? active.scrollOffset.value : 0;
122
+
116
123
  if (isDraggingSheet.value) {
117
- if (isDraggingFromScrollable.value) {
118
- scrollTo(scrollableRef, 0, 0, false);
124
+ if (isDraggingFromScrollable.value && active !== undefined) {
125
+ scrollTo(active.ref, 0, 0, false);
119
126
  }
120
127
  } else {
121
128
  const isDraggingDown = event.translationY > 0;
122
129
  const canStartDrag =
123
- !hasScrollable.value ||
124
- scrollOffset.value <= 0 ||
125
- translateY.value > 0 ||
126
- !isTouchWithinScrollable.value;
130
+ !hasActive || activeOffset <= 0 || translateY.value > 0;
127
131
  if (!canStartDrag || (!isDraggingDown && translateY.value <= 0)) {
128
132
  return;
129
133
  }
130
134
  const isScrollableActive =
131
- hasScrollable.value && isScrollableGestureActive.value;
135
+ hasActive && active !== undefined && active.isGestureActive.value;
132
136
  isDraggingSheet.set(true);
133
- isDraggingFromScrollable.set(
134
- isScrollableActive &&
135
- isTouchWithinScrollable.value &&
136
- scrollOffset.value <= 0
137
- );
137
+ isDraggingFromScrollable.set(isScrollableActive && activeOffset <= 0);
138
138
  dragStartTranslateY.set(translateY.value - event.translationY);
139
- isScrollableLocked.set(hasScrollable.value);
140
- if (
141
- isTouchWithinScrollable.value &&
142
- hasScrollable.value &&
143
- scrollOffset.value <= 0
144
- ) {
145
- scrollTo(scrollableRef, 0, 0, false);
139
+ isScrollableLocked.set(hasActive);
140
+ if (hasActive && active !== undefined && activeOffset <= 0) {
141
+ scrollTo(active.ref, 0, 0, false);
146
142
  }
147
143
  }
148
144
  const rawTranslate = dragStartTranslateY.value + event.translationY;
@@ -163,12 +159,7 @@ export const useBottomSheetPanGesture = ({
163
159
  maxDraggableTranslateY
164
160
  );
165
161
  translateY.set(nextTranslate);
166
- if (
167
- isDraggingSheet.value &&
168
- rawTranslate < 0 &&
169
- isTouchWithinScrollable.value &&
170
- hasScrollable.value
171
- ) {
162
+ if (isDraggingSheet.value && rawTranslate < 0 && hasActive) {
172
163
  isDraggingSheet.set(false);
173
164
  isScrollableLocked.set(false);
174
165
  let targetSnapIndex = -1;