Created
February 10, 2020 23:19
-
-
Save RichardLindhout/81a4b7592e5a53b22f841a3377a823c2 to your computer and use it in GitHub Desktop.
Closable modal scroll friendly React Native
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useRef, useCallback } from 'react' | |
import { | |
View, | |
Keyboard, | |
Animated, | |
ScrollView, | |
TouchableWithoutFeedback, | |
StyleSheet, | |
} from 'react-native' | |
import { Portal } from 'react-native-paper' | |
import widthAndHeightHOC from '../WidthAndHeight/widthAndHeightHOC' | |
import safeAreaHOC from '../WidthAndHeight/safeAreaHOC' | |
import keyboardHOC from './keyboardHOC' | |
function BottomSheet({ | |
keyboardHeight, | |
isVisible, | |
onRequestClose, | |
height, | |
safe, | |
children, | |
}) { | |
const animatedScrollYValue = useRef(new Animated.Value(0)).current | |
const scrollView = React.createRef() | |
const offset = React.createRef(0) | |
const onAnimatedClose = useCallback(() => { | |
if (scrollView.current) { | |
Keyboard.dismiss() | |
scrollView.current && | |
scrollView.current.scrollTo({ y: 0, animated: true }) | |
onRequestClose() | |
} else { | |
onRequestClose() | |
} | |
}, [scrollView, onRequestClose]) | |
const onScrollViewRendered = useCallback( | |
node => { | |
scrollView.current = node | |
if (scrollView.current) { | |
setTimeout(() => { | |
scrollView.current && | |
scrollView.current.scrollTo({ | |
y: height - safe.top, | |
animated: true, | |
}) | |
}, 100) | |
} | |
}, | |
[scrollView, height, safe.top] | |
) | |
const onScroll = useCallback( | |
event => { | |
var currentOffset = event.nativeEvent.contentOffset.y | |
let direction = currentOffset > offset.current ? 'down' : 'up' | |
if (currentOffset === offset.current) { | |
direction = 'idle' | |
} | |
offset.current = currentOffset | |
if (currentOffset <= 0 && (direction === 'up' || direction === 'idle')) { | |
if (isVisible) { | |
onRequestClose() | |
} | |
} | |
}, | |
[offset, isVisible, onRequestClose] | |
) | |
const animatedOpacity = animatedScrollYValue.interpolate({ | |
inputRange: [0, height - safe.top, height], | |
outputRange: [0, 0, 0.4], | |
extrapolate: 'clamp', | |
}) | |
return ( | |
<Portal pointerEvents="none"> | |
{isVisible && ( | |
<> | |
<ScrollView | |
ref={onScrollViewRendered} | |
scrollEventThrottle={25} | |
keyboardShouldPersistTaps="always" | |
style={styles.scrollView} | |
contentInsetAdjustmentBehavior={'never'} | |
alwaysBounceVertical={false} | |
bounces={false} | |
onScroll={Animated.event( | |
[ | |
{ | |
nativeEvent: { contentOffset: { y: animatedScrollYValue } }, | |
}, | |
], | |
{ listener: onScroll } | |
)} | |
> | |
<TouchableWithoutFeedback onPress={onAnimatedClose}> | |
<View | |
style={[ | |
styles.touchableInner, | |
{ | |
height: height, | |
}, | |
]} | |
/> | |
</TouchableWithoutFeedback> | |
<View style={[styles.panel, { minHeight: height }]}> | |
<View style={{ height: safe.top }} /> | |
{children} | |
<View style={{ height: safe.bottom }} /> | |
{keyboardHeight > 0 && ( | |
<View style={{ height: keyboardHeight }} /> | |
)} | |
</View> | |
</ScrollView> | |
<Animated.View | |
style={[ | |
styles.statusBar, | |
{ | |
height: safe.top, | |
opacity: animatedOpacity, | |
}, | |
]} | |
pointerEvents="none" | |
/> | |
</> | |
)} | |
</Portal> | |
) | |
} | |
const styles = StyleSheet.create({ | |
panel: { | |
width: '100%', | |
backgroundColor: '#fff', | |
borderTopStartRadius: 20, | |
borderTopEndRadius: 20, | |
overflow: 'hidden', | |
}, | |
touchableInner: { width: '100%' }, | |
scrollView: { flex: 1, backgroundColor: 'rgba(0,0,0,0.4)' }, | |
statusBar: { | |
backgroundColor: '#000', | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
}, | |
}) | |
export default keyboardHOC(safeAreaHOC(widthAndHeightHOC(BottomSheet))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment