11import { useBackHandler } from '@react-native-community/hooks' ;
22import * as Haptics from 'expo-haptics' ;
3- import React , { forwardRef , isValidElement , useEffect , useImperativeHandle , useRef , useState , useCallback } from 'react' ;
4- import { Keyboard , type LayoutChangeEvent , useWindowDimensions } from 'react-native' ;
5- import { Easing , useDerivedValue , useSharedValue } from 'react-native-reanimated' ;
6- import BottomSheet , { BottomSheetBackdrop , type BottomSheetBackdropProps } from '@discord/bottom-sheet' ;
3+ import React , { forwardRef , isValidElement , useEffect , useImperativeHandle , useRef , useState } from 'react' ;
4+ import { Keyboard , useWindowDimensions } from 'react-native' ;
5+ import { TrueSheet } from '@lodev09/react-native-true-sheet' ;
76import { useSafeAreaInsets } from 'react-native-safe-area-context' ;
87
98import { useTheme } from '../../theme' ;
10- import { isIOS , isTablet } from '../../lib/methods/helpers' ;
9+ import { isTablet } from '../../lib/methods/helpers' ;
1110import { Handle } from './Handle' ;
1211import { type TActionSheetOptions } from './Provider' ;
1312import BottomSheetContent from './BottomSheetContent' ;
1413import styles from './styles' ;
1514
1615export const ACTION_SHEET_ANIMATION_DURATION = 250 ;
17- const HANDLE_HEIGHT = 28 ;
18- const CANCEL_HEIGHT = 64 ;
19-
20- const ANIMATION_CONFIG = {
21- duration : ACTION_SHEET_ANIMATION_DURATION ,
22- // https://easings.net/#easeInOutCubic
23- easing : Easing . bezier ( 0.645 , 0.045 , 0.355 , 1.0 )
24- } ;
2516
2617const ActionSheet = React . memo (
2718 forwardRef ( ( { children } : { children : React . ReactElement } , ref ) => {
2819 const { colors } = useTheme ( ) ;
2920 const { height : windowHeight } = useWindowDimensions ( ) ;
30- const { bottom, right, left } = useSafeAreaInsets ( ) ;
31- const { fontScale } = useWindowDimensions ( ) ;
32- const itemHeight = 48 * fontScale ;
33- const bottomSheetRef = useRef < BottomSheet > ( null ) ;
21+ const { right, left } = useSafeAreaInsets ( ) ;
22+ const sheetRef = useRef < TrueSheet > ( null ) ;
3423 const [ data , setData ] = useState < TActionSheetOptions > ( { } as TActionSheetOptions ) ;
3524 const [ isVisible , setVisible ] = useState ( false ) ;
36- const animatedContentHeight = useSharedValue ( 0 ) ;
37- const animatedHandleHeight = useSharedValue ( 0 ) ;
38- const animatedDataSnaps = useSharedValue < TActionSheetOptions [ 'snaps' ] > ( [ ] ) ;
39- const animatedSnapPoints = useDerivedValue ( ( ) => {
40- if ( animatedDataSnaps . value ?. length ) {
41- return animatedDataSnaps . value ;
42- }
43- const contentWithHandleHeight = animatedContentHeight . value + animatedHandleHeight . value ;
44- // Bottom sheet requires a default value to work
45- if ( contentWithHandleHeight === 0 ) {
46- return [ '25%' ] ;
47- }
48- return [ contentWithHandleHeight ] ;
49- } , [ data ] ) ;
50-
51- const handleContentLayout = useCallback (
52- ( {
53- nativeEvent : {
54- layout : { height }
55- }
56- } : LayoutChangeEvent ) => {
57- /**
58- * This logic is only necessary to prevent the action sheet from
59- * occupying the entire screen when the dynamic content is too big.
60- */
61- animatedContentHeight . value = Math . min ( height , windowHeight * 0.8 ) ;
62- } ,
63- [ animatedContentHeight , windowHeight ]
64- ) ;
65-
66- const maxSnap = Math . min (
67- ( itemHeight + 0.5 ) * ( data ?. options ?. length || 0 ) +
68- HANDLE_HEIGHT +
69- // Custom header height
70- ( data ?. headerHeight || 0 ) +
71- // Insets bottom height (Notch devices)
72- bottom +
73- // Cancel button height
74- ( data ?. hasCancel ? CANCEL_HEIGHT : 0 ) ,
75- windowHeight * 0.8
76- ) ;
77-
78- /*
79- * if the action sheet cover more than 60% of the screen height,
80- * we'll provide more one snap of 50%
81- */
82- const snaps = maxSnap > windowHeight * 0.6 && ! data . snaps ? [ '50%' , maxSnap ] : [ maxSnap ] ;
83-
84- const toggleVisible = ( ) => setVisible ( ! isVisible ) ;
8525
8626 const hide = ( ) => {
87- bottomSheetRef . current ?. close ( ) ;
27+ sheetRef . current ?. dismiss ( ) ;
8828 } ;
8929
9030 const show = ( options : TActionSheetOptions ) => {
9131 setData ( options ) ;
92- if ( options . snaps ?. length ) {
93- animatedDataSnaps . value = options . snaps ;
94- }
95- toggleVisible ( ) ;
32+ setVisible ( true ) ;
33+ sheetRef . current ?. present ( 0 ) ;
9634 } ;
9735
9836 useBackHandler ( ( ) => {
@@ -114,69 +52,43 @@ const ActionSheet = React.memo(
11452 hideActionSheet : hide
11553 } ) ) ;
11654
117- const renderHandle = ( ) => (
55+ const renderHeader = ( ) => (
11856 < >
11957 < Handle />
12058 { isValidElement ( data ?. customHeader ) ? data . customHeader : null }
12159 </ >
12260 ) ;
12361
124- const onClose = ( ) => {
125- toggleVisible ( ) ;
126- data ?. onClose && data ?. onClose ( ) ;
127- animatedDataSnaps . value = [ ] ;
62+ const onDidDismiss = ( ) => {
63+ setVisible ( false ) ;
64+ data ?. onClose ?.( ) ;
12865 } ;
12966
130- const renderBackdrop = useCallback (
131- ( props : BottomSheetBackdropProps ) => (
132- < BottomSheetBackdrop
133- { ...props }
134- appearsOnIndex = { 0 }
135- // Backdrop should be visible all the time bottom sheet is open
136- disappearsOnIndex = { - 1 }
137- opacity = { colors . backdropOpacity }
138- />
139- ) ,
140- [ ]
141- ) ;
142-
143- const bottomSheet = isTablet ? styles . bottomSheet : { marginRight : right , marginLeft : left } ;
144-
145- // Must need this prop to avoid keyboard dismiss
146- // when is android tablet and the input text is focused
147- const androidTablet : any = isTablet && ! isIOS ? { android_keyboardInputMode : 'adjustResize' } : { } ;
67+ const bottomSheetStyle = isTablet ? styles . bottomSheet : { marginRight : right , marginLeft : left } ;
14868
14969 return (
15070 < >
15171 { children }
152- { isVisible && (
153- < BottomSheet
154- ref = { bottomSheetRef }
155- // If data.options exist, we calculate snaps to be precise, otherwise we cal
156- snapPoints = { data . options ?. length ? snaps : animatedSnapPoints }
157- handleHeight = { animatedHandleHeight }
158- // We need undefined to enable vertical swipe gesture inside the bottom sheet like in reaction picker
159- contentHeight = { data . snaps ?. length || data . options ?. length ? undefined : animatedContentHeight }
160- animationConfigs = { ANIMATION_CONFIG }
161- animateOnMount = { true }
162- backdropComponent = { renderBackdrop }
163- handleComponent = { renderHandle }
164- enablePanDownToClose
165- style = { { ...styles . container , ...bottomSheet } }
166- backgroundStyle = { { backgroundColor : colors . surfaceLight } }
167- onChange = { index => index === - 1 && onClose ( ) }
168- // We need this to allow horizontal swipe gesture inside the bottom sheet like in reaction picker
169- enableContentPanningGesture = { data ?. enableContentPanningGesture ?? true }
170- { ...androidTablet } >
171- < BottomSheetContent
172- options = { data ?. options }
173- hide = { hide }
174- children = { data ?. children }
175- hasCancel = { data ?. hasCancel }
176- onLayout = { handleContentLayout }
177- />
178- </ BottomSheet >
179- ) }
72+ < TrueSheet
73+ ref = { sheetRef }
74+ detents = { [ 'auto' ] }
75+ maxHeight = { windowHeight * 0.8 }
76+ backgroundColor = { colors . surfaceLight }
77+ cornerRadius = { 16 }
78+ dimmed
79+ grabber = { false }
80+ header = { renderHeader ( ) }
81+ scrollable = { ! ! data ?. options }
82+ style = { [ styles . container , bottomSheetStyle ] }
83+ onDidDismiss = { onDidDismiss } >
84+ < BottomSheetContent
85+ options = { data ?. options }
86+ hide = { hide }
87+ children = { data ?. children }
88+ hasCancel = { data ?. hasCancel }
89+ onLayout = { ( ) => { } }
90+ />
91+ </ TrueSheet >
18092 </ >
18193 ) ;
18294 } )
0 commit comments