tooot/src/components/BottomSheet.tsx

115 lines
2.8 KiB
TypeScript
Raw Normal View History

2020-11-28 17:07:30 +01:00
import React, { useEffect, useRef } from 'react'
import {
Animated,
Dimensions,
Modal,
PanResponder,
StyleSheet,
View
} from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { useTheme } from 'src/utils/styles/ThemeManager'
2020-11-30 00:24:53 +01:00
import { StyleConstants } from 'src/utils/styles/constants'
2020-12-06 21:42:19 +01:00
import { ButtonRow } from './Button'
2020-11-28 17:07:30 +01:00
export interface Props {
children: React.ReactNode
visible: boolean
handleDismiss: () => void
}
const BottomSheet: React.FC<Props> = ({ children, visible, handleDismiss }) => {
const { theme } = useTheme()
const insets = useSafeAreaInsets()
const panY = useRef(new Animated.Value(Dimensions.get('screen').height))
.current
const top = panY.interpolate({
inputRange: [-1, 0, 1],
outputRange: [0, 0, 1]
})
const resetModal = Animated.timing(panY, {
toValue: 0,
duration: 300,
useNativeDriver: false
})
const closeModal = Animated.timing(panY, {
toValue: Dimensions.get('screen').height,
duration: 350,
useNativeDriver: false
})
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, { dy: panY }], {
useNativeDriver: false
}),
onPanResponderRelease: (e, gs) => {
if (gs.dy > 0 && gs.vy > 1) {
return closeModal.start(() => handleDismiss())
} else if (gs.dy === 0 && gs.vy === 0) {
return closeModal.start(() => handleDismiss())
}
return resetModal.start()
}
})
).current
useEffect(() => {
if (visible) {
resetModal.start()
}
}, [visible])
return (
<Modal animated animationType='fade' visible={visible} transparent>
<View
style={[styles.overlay, { backgroundColor: theme.border }]}
{...panResponder.panHandlers}
>
<Animated.View
style={[
styles.container,
{
top,
backgroundColor: theme.background,
2020-12-01 00:44:28 +01:00
paddingBottom: insets.bottom || StyleConstants.Spacing.L
2020-11-28 17:07:30 +01:00
}
]}
>
<View
style={[styles.handle, { backgroundColor: theme.background }]}
/>
{children}
2020-12-06 21:42:19 +01:00
<ButtonRow
2020-11-28 17:07:30 +01:00
onPress={() => closeModal.start(() => handleDismiss())}
2020-12-03 22:03:06 +01:00
text='取消'
/>
2020-11-28 17:07:30 +01:00
</Animated.View>
</View>
</Modal>
)
}
const styles = StyleSheet.create({
overlay: {
flex: 1,
justifyContent: 'flex-end'
},
container: {
2020-11-30 00:24:53 +01:00
paddingTop: StyleConstants.Spacing.M
2020-11-28 17:07:30 +01:00
},
handle: {
alignSelf: 'center',
2020-12-01 00:44:28 +01:00
width: StyleConstants.Spacing.S * 8,
height: StyleConstants.Spacing.S / 2,
2020-11-28 17:07:30 +01:00
borderRadius: 100,
2020-11-30 00:24:53 +01:00
top: -StyleConstants.Spacing.M * 2
2020-11-28 17:07:30 +01:00
}
})
export default BottomSheet