Skip to content

Commit 1be2b44

Browse files
author
Will
committed
feat: integrate react-native-safe-area-context for improved modal handling
1 parent 7a89724 commit 1be2b44

6 files changed

Lines changed: 189 additions & 125 deletions

File tree

lib/components/BottomSheetModal/index.tsx

Lines changed: 86 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable react-native/no-inline-styles */
2-
import React, {useEffect, useMemo, useRef, useState} from 'react';
2+
import React, { useEffect, useMemo, useRef, useState } from 'react';
33
import {
44
Animated,
55
Modal,
@@ -10,9 +10,13 @@ import {
1010
Keyboard,
1111
NativeSyntheticEvent,
1212
} from 'react-native';
13+
import {
14+
SafeAreaProvider,
15+
SafeAreaView,
16+
} from 'react-native-safe-area-context';
1317

1418
import parseHeight from '../../utils/parseHeight';
15-
import {ICountrySelectStyle} from '../../interface';
19+
import { ICountrySelectStyle } from '../../interface';
1620

1721
interface BottomSheetModalProps extends ModalProps {
1822
visible: boolean;
@@ -67,19 +71,26 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
6771
const dragStartYRef = useRef(0);
6872

6973
useEffect(() => {
70-
const DRAG_HANDLE_HEIGHT = 20;
71-
const availableHeight = Math.max(0, modalHeight - DRAG_HANDLE_HEIGHT);
72-
const parsedMinHeight = parseHeight(minBottomsheetHeight, availableHeight);
73-
const parsedMaxHeight = parseHeight(maxBottomsheetHeight, availableHeight);
74+
const parsedMinHeight = parseHeight(
75+
minBottomsheetHeight,
76+
modalHeight
77+
);
78+
const parsedMaxHeight = parseHeight(
79+
maxBottomsheetHeight,
80+
modalHeight
81+
);
7482
const parsedInitialHeight = parseHeight(
7583
initialBottomsheetHeight,
76-
availableHeight,
84+
modalHeight
7785
);
7886
setBottomSheetSize({
79-
minHeight: parsedMinHeight || MIN_HEIGHT_PERCENTAGE * availableHeight,
80-
maxHeight: parsedMaxHeight || MAX_HEIGHT_PERCENTAGE * availableHeight,
87+
minHeight:
88+
parsedMinHeight || MIN_HEIGHT_PERCENTAGE * modalHeight,
89+
maxHeight:
90+
parsedMaxHeight || MAX_HEIGHT_PERCENTAGE * modalHeight,
8191
initialHeight:
82-
parsedInitialHeight || INITIAL_HEIGHT_PERCENTAGE * availableHeight,
92+
parsedInitialHeight ||
93+
INITIAL_HEIGHT_PERCENTAGE * modalHeight,
8394
});
8495
}, [
8596
modalHeight,
@@ -115,17 +126,17 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
115126
onStartShouldSetPanResponder: () => true,
116127
onMoveShouldSetPanResponder: (_evt, gestureState) =>
117128
Math.abs(gestureState.dy) > 5,
118-
onPanResponderGrant: e => {
129+
onPanResponderGrant: (e) => {
119130
dragStartYRef.current = e.nativeEvent.pageY;
120131
sheetHeight.stopAnimation();
121132
},
122-
onPanResponderMove: e => {
133+
onPanResponderMove: (e) => {
123134
const currentY = e.nativeEvent.pageY;
124135
const dy = currentY - dragStartYRef.current;
125136
const proposedHeight = lastHeightRef.current - dy;
126137
sheetHeight.setValue(proposedHeight);
127138
},
128-
onPanResponderRelease: e => {
139+
onPanResponderRelease: (e) => {
129140
const currentY = e.nativeEvent.pageY;
130141
const dy = currentY - dragStartYRef.current;
131142
const currentHeight = lastHeightRef.current - dy;
@@ -134,12 +145,14 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
134145
toValue: 0,
135146
duration: 200,
136147
useNativeDriver: false,
137-
}).start(() => onRequestClose({} as NativeSyntheticEvent<any>));
148+
}).start(() =>
149+
onRequestClose({} as NativeSyntheticEvent<any>)
150+
);
138151
return;
139152
}
140153
const finalHeight = Math.min(
141154
Math.max(currentHeight, bottomSheetSize.minHeight),
142-
bottomSheetSize.maxHeight,
155+
bottomSheetSize.maxHeight
143156
);
144157
Animated.spring(sheetHeight, {
145158
toValue: finalHeight,
@@ -159,7 +172,7 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
159172
}).start();
160173
},
161174
}),
162-
[bottomSheetSize, sheetHeight, onRequestClose],
175+
[bottomSheetSize, sheetHeight, onRequestClose]
163176
);
164177
return (
165178
<Modal
@@ -168,56 +181,68 @@ export const BottomSheetModal: React.FC<BottomSheetModalProps> = ({
168181
animationType="slide"
169182
onRequestClose={onRequestClose}
170183
statusBarTranslucent={statusBarTranslucent}
171-
{...props}>
172-
<View
173-
testID="countrySelectContainer"
174-
style={[styles.container, countrySelectStyle?.container]}
175-
onLayout={e => setModalHeight(e.nativeEvent.layout.height)}>
176-
<Pressable
177-
testID="countrySelectBackdrop"
178-
accessibilityRole="button"
179-
accessibilityLabel={accessibilityLabelBackdrop}
180-
accessibilityHint={accessibilityHintBackdrop}
181-
disabled={disabledBackdropPress || removedBackdrop}
182-
style={[
183-
styles.backdrop,
184-
countrySelectStyle?.backdrop,
185-
removedBackdrop && {backgroundColor: 'transparent'},
186-
]}
187-
onPress={onBackdropPress || onRequestClose}
188-
/>
189-
<Animated.View
190-
testID="countrySelectContent"
191-
style={[
192-
styles.content,
193-
countrySelectStyle?.content,
194-
{
195-
height: sheetHeight,
196-
},
197-
]}>
184+
{...props}
185+
>
186+
<SafeAreaProvider>
187+
<SafeAreaView style={{ flex: 1 }}>
198188
<View
199-
{...panHandlers.panHandlers}
200-
style={[
201-
styles.dragHandleContainer,
202-
countrySelectStyle?.dragHandleContainer,
203-
]}>
204-
{dragHandleIndicatorComponent ? (
205-
dragHandleIndicatorComponent()
206-
) : (
189+
testID="countrySelectContainer"
190+
style={[styles.container, countrySelectStyle?.container]}
191+
onLayout={(e) =>
192+
setModalHeight(e.nativeEvent.layout.height)
193+
}
194+
>
195+
<Pressable
196+
testID="countrySelectBackdrop"
197+
accessibilityRole="button"
198+
accessibilityLabel={accessibilityLabelBackdrop}
199+
accessibilityHint={accessibilityHintBackdrop}
200+
disabled={disabledBackdropPress || removedBackdrop}
201+
style={[
202+
styles.backdrop,
203+
countrySelectStyle?.backdrop,
204+
removedBackdrop && { backgroundColor: 'transparent' },
205+
]}
206+
onPress={onBackdropPress || onRequestClose}
207+
/>
208+
<Animated.View
209+
testID="countrySelectContent"
210+
style={[
211+
styles.content,
212+
countrySelectStyle?.content,
213+
{
214+
height: sheetHeight,
215+
},
216+
]}
217+
>
207218
<View
219+
{...panHandlers.panHandlers}
208220
style={[
209-
styles.dragHandleIndicator,
210-
countrySelectStyle?.dragHandleIndicator,
221+
styles.dragHandleContainer,
222+
countrySelectStyle?.dragHandleContainer,
211223
]}
212-
/>
213-
)}
224+
>
225+
{dragHandleIndicatorComponent ? (
226+
dragHandleIndicatorComponent()
227+
) : (
228+
<View
229+
style={[
230+
styles.dragHandleIndicator,
231+
countrySelectStyle?.dragHandleIndicator,
232+
]}
233+
/>
234+
)}
235+
</View>
236+
{header}
237+
<Animated.View
238+
style={{ flex: 1, flexDirection: 'row' }}
239+
>
240+
{children}
241+
</Animated.View>
242+
</Animated.View>
214243
</View>
215-
{header}
216-
<Animated.View style={{flex: 1, flexDirection: 'row'}}>
217-
{children}
218-
</Animated.View>
219-
</Animated.View>
220-
</View>
244+
</SafeAreaView>
245+
</SafeAreaProvider>
221246
</Modal>
222247
);
223248
};

lib/components/FullscreenModal/index.tsx

Lines changed: 47 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import {
77
Pressable,
88
View,
99
} from 'react-native';
10+
import {
11+
SafeAreaProvider,
12+
SafeAreaView,
13+
} from 'react-native-safe-area-context';
1014

11-
import {ICountrySelectStyle} from '../../interface';
15+
import { ICountrySelectStyle } from '../../interface';
1216

1317
interface FullscreenModalProps extends ModalProps {
1418
visible: boolean;
@@ -47,39 +51,48 @@ export const FullscreenModal: React.FC<FullscreenModalProps> = ({
4751
animationType="fade"
4852
onRequestClose={onRequestClose}
4953
statusBarTranslucent={statusBarTranslucent}
50-
{...props}>
51-
<View
52-
testID="countrySelectContainer"
53-
style={[
54-
styles.container,
55-
countrySelectStyle?.container,
56-
{flex: 1, width: '100%', height: '100%'},
57-
]}>
58-
<Pressable
59-
testID="countrySelectBackdrop"
60-
accessibilityRole="button"
61-
accessibilityLabel={accessibilityLabelBackdrop}
62-
accessibilityHint={accessibilityHintBackdrop}
63-
disabled={disabledBackdropPress || removedBackdrop}
64-
style={[
65-
styles.backdrop,
66-
{alignItems: 'center', justifyContent: 'center'},
67-
countrySelectStyle?.backdrop,
68-
removedBackdrop && {backgroundColor: 'transparent'},
69-
]}
70-
onPress={onBackdropPress || onRequestClose}
71-
/>
72-
<View
73-
testID="countrySelectContent"
74-
style={[
75-
styles.content,
76-
countrySelectStyle?.content,
77-
{borderRadius: 0, width: '100%', height: '100%'},
78-
]}>
79-
{header}
80-
<View style={{flex: 1, flexDirection: 'row'}}>{children}</View>
81-
</View>
82-
</View>
54+
{...props}
55+
>
56+
<SafeAreaProvider>
57+
<SafeAreaView style={{ flex: 1 }}>
58+
<View
59+
testID="countrySelectContainer"
60+
style={[
61+
styles.container,
62+
countrySelectStyle?.container,
63+
{ flex: 1, width: '100%', height: '100%' },
64+
]}
65+
>
66+
<Pressable
67+
testID="countrySelectBackdrop"
68+
accessibilityRole="button"
69+
accessibilityLabel={accessibilityLabelBackdrop}
70+
accessibilityHint={accessibilityHintBackdrop}
71+
disabled={disabledBackdropPress || removedBackdrop}
72+
style={[
73+
styles.backdrop,
74+
{ alignItems: 'center', justifyContent: 'center' },
75+
countrySelectStyle?.backdrop,
76+
removedBackdrop && { backgroundColor: 'transparent' },
77+
]}
78+
onPress={onBackdropPress || onRequestClose}
79+
/>
80+
<View
81+
testID="countrySelectContent"
82+
style={[
83+
styles.content,
84+
countrySelectStyle?.content,
85+
{ borderRadius: 0, width: '100%', height: '100%' },
86+
]}
87+
>
88+
{header}
89+
<View style={{ flex: 1, flexDirection: 'row' }}>
90+
{children}
91+
</View>
92+
</View>
93+
</View>
94+
</SafeAreaView>
95+
</SafeAreaProvider>
8396
</Modal>
8497
);
8598
};

lib/components/PopupModal/index.tsx

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import {
77
Pressable,
88
View,
99
} from 'react-native';
10+
import {
11+
SafeAreaProvider,
12+
SafeAreaView,
13+
} from 'react-native-safe-area-context';
1014

11-
import {ICountrySelectStyle} from '../../interface';
15+
import { ICountrySelectStyle } from '../../interface';
1216

1317
interface PopupModalProps extends ModalProps {
1418
visible: boolean;
@@ -47,31 +51,40 @@ export const PopupModal: React.FC<PopupModalProps> = ({
4751
animationType="fade"
4852
onRequestClose={onRequestClose}
4953
statusBarTranslucent={statusBarTranslucent}
50-
{...props}>
51-
<View
52-
testID="countrySelectContainer"
53-
style={[styles.container, countrySelectStyle?.container]}>
54-
<Pressable
55-
testID="countrySelectBackdrop"
56-
accessibilityRole="button"
57-
accessibilityLabel={accessibilityLabelBackdrop}
58-
accessibilityHint={accessibilityHintBackdrop}
59-
disabled={disabledBackdropPress || removedBackdrop}
60-
style={[
61-
styles.backdrop,
62-
{alignItems: 'center', justifyContent: 'center'},
63-
countrySelectStyle?.backdrop,
64-
removedBackdrop && {backgroundColor: 'transparent'},
65-
]}
66-
onPress={onBackdropPress || onRequestClose}
67-
/>
68-
<View
69-
testID="countrySelectContent"
70-
style={[styles.content, countrySelectStyle?.content]}>
71-
{header}
72-
<View style={{flex: 1, flexDirection: 'row'}}>{children}</View>
73-
</View>
74-
</View>
54+
{...props}
55+
>
56+
<SafeAreaProvider>
57+
<SafeAreaView style={{ flex: 1 }}>
58+
<View
59+
testID="countrySelectContainer"
60+
style={[styles.container, countrySelectStyle?.container]}
61+
>
62+
<Pressable
63+
testID="countrySelectBackdrop"
64+
accessibilityRole="button"
65+
accessibilityLabel={accessibilityLabelBackdrop}
66+
accessibilityHint={accessibilityHintBackdrop}
67+
disabled={disabledBackdropPress || removedBackdrop}
68+
style={[
69+
styles.backdrop,
70+
{ alignItems: 'center', justifyContent: 'center' },
71+
countrySelectStyle?.backdrop,
72+
removedBackdrop && { backgroundColor: 'transparent' },
73+
]}
74+
onPress={onBackdropPress || onRequestClose}
75+
/>
76+
<View
77+
testID="countrySelectContent"
78+
style={[styles.content, countrySelectStyle?.content]}
79+
>
80+
{header}
81+
<View style={{ flex: 1, flexDirection: 'row' }}>
82+
{children}
83+
</View>
84+
</View>
85+
</View>
86+
</SafeAreaView>
87+
</SafeAreaProvider>
7588
</Modal>
7689
);
7790
};

0 commit comments

Comments
 (0)