1- import { ReactElement , ReactNode , useCallback , useEffect , useMemo , useRef , useState } from "react" ;
2- import { Dimensions , LayoutChangeEvent , Modal , Pressable } from "react-native" ;
1+ import { ReactElement , ReactNode , useCallback , useRef , useState } from "react" ;
2+ import { Modal , Pressable , useWindowDimensions } from "react-native" ;
33import BottomSheet , {
44 BottomSheetBackdrop ,
55 BottomSheetBackdropProps ,
@@ -16,36 +16,50 @@ interface CustomModalSheetProps {
1616
1717export const CustomModalSheet = ( props : CustomModalSheetProps ) : ReactElement => {
1818 const bottomSheetRef = useRef < BottomSheet > ( null ) ;
19- const [ contentHeight , setContentHeight ] = useState ( 0 ) ;
20- const [ currentStatus , setCurrentStatus ] = useState ( false ) ;
19+ const { height : windowHeight } = useWindowDimensions ( ) ;
2120
22- const isAvailable = props . triggerAttribute && props . triggerAttribute . status === ValueStatus . Available ;
21+ const externalOpen =
22+ props . triggerAttribute ?. status === ValueStatus . Available && props . triggerAttribute . value === true ;
2323
24- const isOpen =
25- props . triggerAttribute &&
26- props . triggerAttribute . status === ValueStatus . Available &&
27- props . triggerAttribute . value ;
24+ const [ mounted , setMounted ] = useState ( externalOpen ) ;
25+ const [ ready , setReady ] = useState ( false ) ;
26+ const didOpenRef = useRef ( false ) ;
2827
29- const onContentLayoutHandler = useCallback (
30- ( event : LayoutChangeEvent ) : void => {
31- const layoutHeight = event . nativeEvent . layout . height ;
32- if ( layoutHeight > 0 && layoutHeight !== contentHeight ) {
33- setContentHeight ( layoutHeight ) ;
34- }
35- } ,
36- [ contentHeight ]
37- ) ;
28+ if ( externalOpen && ! mounted ) {
29+ setMounted ( true ) ;
30+ }
3831
3932 const close = useCallback ( ( ) => {
4033 bottomSheetRef . current ?. close ( ) ;
4134 } , [ ] ) ;
4235
36+ const handleModalShow = useCallback ( ( ) => {
37+ setReady ( true ) ;
38+ } , [ ] ) ;
39+
40+ const handleChange = useCallback (
41+ ( index : number ) => {
42+ if ( index === 0 ) {
43+ didOpenRef . current = true ;
44+ return ;
45+ }
46+
47+ if ( index === - 1 && didOpenRef . current ) {
48+ didOpenRef . current = false ;
49+ setReady ( false ) ;
50+ props . triggerAttribute ?. setValue ( false ) ;
51+ setMounted ( false ) ;
52+ }
53+ } ,
54+ [ props . triggerAttribute ]
55+ ) ;
56+
4357 const renderBackdrop = useCallback (
4458 ( backdropProps : BottomSheetBackdropProps ) => (
4559 < Pressable style = { { flex : 1 } } onPress = { close } >
4660 < BottomSheetBackdrop
4761 { ...backdropProps }
48- pressBehavior = { "close" }
62+ pressBehavior = "close"
4963 opacity = { 0.3 }
5064 appearsOnIndex = { 0 }
5165 disappearsOnIndex = { - 1 }
@@ -55,72 +69,32 @@ export const CustomModalSheet = (props: CustomModalSheetProps): ReactElement =>
5569 [ close ]
5670 ) ;
5771
58- const snapPoints = useMemo ( ( ) => {
59- if ( contentHeight === 0 ) {
60- return [ 1 ] ; // During measurement
61- }
62-
63- // Use actual measured content height, cap at 90% screen
64- const maxHeight = Dimensions . get ( "screen" ) . height * 0.9 ;
65- const snapHeight = Math . min ( contentHeight , maxHeight ) ;
66- return [ snapHeight ] ;
67- } , [ contentHeight ] ) ;
68-
69- const handleSheetChanges = useCallback (
70- ( index : number ) => {
71- if ( ! isAvailable ) {
72- return ;
73- }
74-
75- const hasClosed = index === - 1 ;
76- if ( hasClosed && props . triggerAttribute ?. value ) {
77- props . triggerAttribute ?. setValue ( false ) ;
78- setCurrentStatus ( false ) ;
79- }
80- } ,
81- [ isAvailable , props . triggerAttribute ]
82- ) ;
83-
84- useEffect ( ( ) => {
85- if ( ! isAvailable ) {
86- return ;
87- }
88-
89- const shouldBeOpen = props . triggerAttribute ?. value === true ;
90-
91- if ( shouldBeOpen && ! currentStatus ) {
92- requestAnimationFrame ( ( ) => {
93- setCurrentStatus ( true ) ;
94- } ) ;
95- } else if ( ! shouldBeOpen && currentStatus ) {
96- bottomSheetRef . current ?. close ( ) ;
97- setCurrentStatus ( false ) ;
98- }
99- } , [ props . triggerAttribute ?. value , currentStatus , isAvailable ] ) ;
72+ const maxHeight = windowHeight * 0.9 ;
10073
10174 return (
102- < Modal onRequestClose = { close } transparent visible = { ! ! isOpen } >
103- < BottomSheet
104- ref = { bottomSheetRef }
105- index = { isOpen ? 0 : - 1 }
106- snapPoints = { snapPoints }
107- onClose = { ( ) => handleSheetChanges ( - 1 ) }
108- onChange = { handleSheetChanges }
109- backdropComponent = { renderBackdrop }
110- style = { [ props . styles . modal ] }
111- backgroundStyle = { props . styles . container }
112- enablePanDownToClose = { false }
113- handleComponent = { null }
114- handleStyle = { { display : "none" } }
115- >
116- < BottomSheetScrollView
117- style = { [ { flex : 1 } ] }
118- contentContainerStyle = { { paddingBottom : 16 } }
119- onLayout = { onContentLayoutHandler }
75+ < Modal transparent animationType = "none" visible = { mounted } onRequestClose = { close } onShow = { handleModalShow } >
76+ { ready && (
77+ < BottomSheet
78+ ref = { bottomSheetRef }
79+ index = { 0 }
80+ animateOnMount
81+ enableDynamicSizing
82+ maxDynamicContentSize = { maxHeight }
83+ containerHeight = { windowHeight }
84+ enablePanDownToClose
85+ onChange = { handleChange }
86+ onClose = { ( ) => handleChange ( - 1 ) }
87+ backdropComponent = { renderBackdrop }
88+ style = { [ props . styles . modal ] }
89+ backgroundStyle = { props . styles . container }
90+ handleComponent = { null }
91+ handleStyle = { { display : "none" } }
12092 >
121- { props . content }
122- </ BottomSheetScrollView >
123- </ BottomSheet >
93+ < BottomSheetScrollView style = { [ { flex : 1 } ] } contentContainerStyle = { { paddingBottom : 16 } } >
94+ { props . content }
95+ </ BottomSheetScrollView >
96+ </ BottomSheet >
97+ ) }
12498 </ Modal >
12599 ) ;
126100} ;
0 commit comments