1- /***********************************************************************************
2- * This component wraps the @gorhom/bottom-sheet library
3- * to more easily take advantage of it throughout the app.
4- *
5- * Implementation:
6- * const snapPoints = useSnapPoints('medium');
7- *
8- * <BottomSheetWrapper view="viewName" snapPoints={snapPoints}>
9- * <View>...</View>
10- * </BottomSheetWrapper>
11- *
12- * Usage Throughout App:
13- * dispatch(showBottomSheet('viewName'));
14- * dispatch(showBottomSheet('viewName', { option1: 'value' }));
15- * dispatch(closeSheet('viewName'));
16- *
17- * Check if a given view is open:
18- * getStore().user.viewController['viewName'].isOpen;
19- ***********************************************************************************/
20-
21- import BottomSheet , {
22- BottomSheetView ,
1+ import {
232 BottomSheetBackdrop ,
24- BottomSheetBackgroundProps ,
253 BottomSheetBackdropProps ,
4+ BottomSheetBackgroundProps ,
5+ BottomSheetModal ,
6+ BottomSheetView ,
267} from '@gorhom/bottom-sheet' ;
27- import React , {
28- memo ,
29- ReactElement ,
30- forwardRef ,
31- useImperativeHandle ,
32- useRef ,
33- useEffect ,
34- useCallback ,
35- useMemo ,
36- useState ,
37- } from 'react' ;
8+ import React , { ReactNode , useCallback , useEffect , useMemo } from 'react' ;
389import { StyleSheet } from 'react-native' ;
3910import { useReducedMotion } from 'react-native-reanimated' ;
40- import { useTheme } from 'styled-components/native' ;
4111
4212import { __E2E__ } from '../constants/env' ;
13+ import useColors from '../hooks/colors' ;
4314import { useAppDispatch , useAppSelector } from '../hooks/redux' ;
15+ import {
16+ SheetId ,
17+ useSheetRef ,
18+ } from '../navigation/bottom-sheet/SheetRefsProvider' ;
4419import { viewControllerSelector } from '../store/reselect/ui' ;
45- import { closeSheet } from '../store/slices/ui' ;
46- import { TViewController } from '../store/types/ui' ;
20+ import { resetSheet } from '../store/slices/ui' ;
4721import BottomSheetBackground from './BottomSheetBackground' ;
4822
49- export interface BottomSheetWrapperProps {
50- children : ReactElement ;
51- view : TViewController ;
23+ type SheetProps = {
24+ view : SheetId ;
5225 snapPoints : number [ ] ;
53- backdrop ?: boolean ;
26+ children : ReactNode ;
5427 testID ?: string ;
5528 onOpen ?: ( ) => void ;
5629 onClose ?: ( ) => void ;
57- }
58-
59- const BottomSheetWrapper = forwardRef (
60- (
61- {
62- children,
63- view,
64- snapPoints,
65- backdrop = true ,
66- testID,
67- onOpen,
68- onClose,
69- } : BottomSheetWrapperProps ,
70- ref ,
71- ) : ReactElement => {
72- const bottomSheetRef = useRef < BottomSheet > ( null ) ;
73- const reducedMotion = useReducedMotion ( ) ;
74- const dispatch = useAppDispatch ( ) ;
75- const data = useAppSelector ( ( state ) => viewControllerSelector ( state , view ) ) ;
76- const theme = useTheme ( ) ;
77- const handleIndicatorStyle = useMemo (
78- ( ) => ( { backgroundColor : theme . colors . gray2 } ) ,
79- [ theme . colors . gray2 ] ,
80- ) ;
81- const [ mounted , setMounted ] = useState ( false ) ;
82-
83- // https://github.com/gorhom/react-native-bottom-sheet/issues/770#issuecomment-1072113936
84- // do not activate BottomSheet if swipe horizontally, this allows using Swiper inside of it
85- const activeOffsetX = useMemo ( ( ) => [ - 999 , 999 ] , [ ] ) ;
86- const activeOffsetY = useMemo ( ( ) => [ - 10 , 10 ] , [ ] ) ;
87-
88- useEffect ( ( ) => {
89- if ( data . isOpen ) {
90- bottomSheetRef . current ?. snapToIndex ( 0 ) ;
91- } else {
92- bottomSheetRef . current ?. close ( ) ;
93- }
94- setTimeout ( ( ) => setMounted ( true ) , 500 ) ;
95- } , [ data . isOpen ] ) ;
96-
97- useImperativeHandle ( ref , ( ) => ( {
98- snapToIndex ( index = 0 ) : void {
99- bottomSheetRef . current ?. snapToIndex ( index ) ;
100- } ,
101- expand ( ) : void {
102- bottomSheetRef . current ?. snapToIndex ( 1 ) ;
103- } ,
104- close ( ) : void {
105- bottomSheetRef . current ?. close ( ) ;
106- } ,
107- } ) ) ;
108-
109- const _onOpen = useCallback ( ( ) => onOpen ?.( ) , [ onOpen ] ) ;
110-
111- const _onClose = useCallback ( ( ) => {
112- if ( data . isOpen ) {
113- dispatch ( closeSheet ( view ) ) ;
114- }
115- onClose ?.( ) ;
116- } , [ data . isOpen , view , onClose , dispatch ] ) ;
117-
118- // callbacks
119- const handleSheetChanges = useCallback (
120- ( index : number ) => {
121- if ( index === - 1 ) {
122- _onClose ( ) ;
123- } else if ( index >= 0 ) {
124- _onOpen ( ) ;
125- }
126- } ,
127- [ _onClose , _onOpen ] ,
128- ) ;
129-
130- const renderBackdrop = useCallback (
131- ( props : BottomSheetBackdropProps ) => {
132- if ( ! backdrop ) {
133- return null ;
134- }
135- return (
136- < BottomSheetBackdrop
137- { ...props }
138- disappearsOnIndex = { - 1 }
139- appearsOnIndex = { 0 }
140- accessibilityLabel = "Close"
141- />
142- ) ;
143- } ,
144- [ backdrop ] ,
145- ) ;
146-
147- const backgroundComponent = useCallback (
148- ( { style } : BottomSheetBackgroundProps ) => (
149- < BottomSheetBackground style = { style } />
150- ) ,
151- [ ] ,
152- ) ;
153-
154- const style = useMemo (
155- ( ) => [ styles . container , ! mounted && { minHeight : snapPoints [ 0 ] - 30 } ] ,
156- [ snapPoints , mounted ] ,
157- ) ;
158-
159- // Determine initial snapPoint index based on provided data.
160- const index = useMemo ( ( ) : number => ( data . isOpen ? 0 : - 1 ) , [ data . isOpen ] ) ;
161-
30+ } ;
31+
32+ const Sheet = ( {
33+ view,
34+ snapPoints,
35+ children,
36+ testID,
37+ onOpen,
38+ onClose,
39+ } : SheetProps ) => {
40+ const colors = useColors ( ) ;
41+ const sheetRef = useSheetRef ( view ) ;
42+ const isReducedMotion = useReducedMotion ( ) ;
43+ const dispatch = useAppDispatch ( ) ;
44+ const data = useAppSelector ( ( state ) => viewControllerSelector ( state , view ) ) ;
45+
46+ // https://github.com/gorhom/react-native-bottom-sheet/issues/770#issuecomment-1072113936
47+ // do not activate BottomSheet if swipe horizontally, this allows using Swiper inside of it
48+ const activeOffsetX = useMemo ( ( ) => [ - 999 , 999 ] , [ ] ) ;
49+ const activeOffsetY = useMemo ( ( ) => [ - 10 , 10 ] , [ ] ) ;
50+
51+ const backdropComponent = useCallback ( ( props : BottomSheetBackdropProps ) => {
16252 return (
163- < BottomSheet
164- ref = { bottomSheetRef }
165- backgroundComponent = { backgroundComponent }
166- backdropComponent = { renderBackdrop }
167- handleIndicatorStyle = { handleIndicatorStyle }
168- handleStyle = { styles . handle }
169- index = { index }
170- snapPoints = { snapPoints }
171- animateOnMount = { ! reducedMotion && ! __E2E__ }
172- enablePanDownToClose = { true }
173- keyboardBlurBehavior = "restore"
174- // @ts -ignore
175- activeOffsetX = { activeOffsetX }
176- // @ts -ignore
177- activeOffsetY = { activeOffsetY }
178- onChange = { handleSheetChanges } >
179- < BottomSheetView style = { style } testID = { testID } >
180- { children }
181- </ BottomSheetView >
182- </ BottomSheet >
53+ < BottomSheetBackdrop
54+ { ...props }
55+ disappearsOnIndex = { - 1 }
56+ appearsOnIndex = { 0 }
57+ accessibilityLabel = "Close"
58+ />
18359 ) ;
184- } ,
185- ) ;
60+ } , [ ] ) ;
61+
62+ const backgroundComponent = useCallback (
63+ ( { style } : BottomSheetBackgroundProps ) => (
64+ < BottomSheetBackground style = { style } />
65+ ) ,
66+ [ ] ,
67+ ) ;
68+
69+ // biome-ignore lint/correctness/useExhaustiveDependencies: sheetRef doesn't change
70+ useEffect ( ( ) => {
71+ if ( data . isOpen ) {
72+ sheetRef . current ?. present ( ) ;
73+ } else {
74+ sheetRef . current ?. close ( ) ;
75+ }
76+ // setTimeout(() => setMounted(true), 500);
77+ } , [ data . isOpen ] ) ;
78+
79+ const onChange = useCallback (
80+ ( index : number ) => {
81+ if ( index === - 1 ) {
82+ onClose ?.( ) ;
83+ // clear sheet params
84+ dispatch ( resetSheet ( view ) ) ;
85+ } else if ( index >= 0 ) {
86+ onOpen ?.( ) ;
87+ }
88+ } ,
89+ [ onOpen , onClose , dispatch , view ] ,
90+ ) ;
91+
92+ return (
93+ < BottomSheetModal
94+ name = { view }
95+ ref = { sheetRef }
96+ snapPoints = { snapPoints }
97+ handleStyle = { styles . handle }
98+ handleIndicatorStyle = { { backgroundColor : colors . gray2 } }
99+ backdropComponent = { backdropComponent }
100+ backgroundComponent = { backgroundComponent }
101+ stackBehavior = "push"
102+ animateOnMount = { ! isReducedMotion && ! __E2E__ }
103+ enablePanDownToClose = { true }
104+ keyboardBlurBehavior = "restore"
105+ // @ts -ignore
106+ activeOffsetX = { activeOffsetX }
107+ // @ts -ignore
108+ activeOffsetY = { activeOffsetY }
109+ onChange = { onChange } >
110+ < BottomSheetView style = { styles . container } testID = { testID } >
111+ { children }
112+ </ BottomSheetView >
113+ </ BottomSheetModal >
114+ ) ;
115+ } ;
186116
187117const styles = StyleSheet . create ( {
188118 container : {
@@ -198,4 +128,4 @@ const styles = StyleSheet.create({
198128 } ,
199129} ) ;
200130
201- export default memo ( BottomSheetWrapper ) ;
131+ export default Sheet ;
0 commit comments