tooot/src/components/BottomSheet.tsx

130 lines
3.3 KiB
TypeScript
Raw Normal View History

2021-01-19 01:13:45 +01:00
import React from 'react'
import { Dimensions, Modal, StyleSheet, View } from 'react-native'
2020-11-28 17:07:30 +01:00
import { useSafeAreaInsets } from 'react-native-safe-area-context'
2020-12-13 14:04:25 +01:00
import { useTheme } from '@utils/styles/ThemeManager'
import { StyleConstants } from '@utils/styles/constants'
2020-12-26 23:05:17 +01:00
import Button from '@components/Button'
2021-01-14 22:53:01 +01:00
import {
PanGestureHandler,
State,
TapGestureHandler
} from 'react-native-gesture-handler'
import Animated, {
Extrapolate,
interpolate,
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withTiming
} from 'react-native-reanimated'
2021-01-24 02:25:43 +01:00
import analytics from './analytics'
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 screenHeight = Dimensions.get('screen').height
const panY = useSharedValue(0)
const styleTop = useAnimatedStyle(() => {
return {
top: interpolate(
panY.value,
[0, screenHeight],
[0, screenHeight],
Extrapolate.CLAMP
)
}
2020-11-28 17:07:30 +01:00
})
const callDismiss = () => {
2021-01-24 02:25:43 +01:00
analytics('bottomsheet_swipe_close')
handleDismiss()
}
const onGestureEvent = useAnimatedGestureHandler({
onActive: ({ translationY }) => {
panY.value = translationY
},
onEnd: ({ velocityY }) => {
if (velocityY > 500) {
runOnJS(callDismiss)()
} else {
panY.value = withTiming(0)
2020-11-28 17:07:30 +01:00
}
}
})
2020-11-28 17:07:30 +01:00
return (
<Modal animated animationType='fade' visible={visible} transparent>
2021-01-14 22:53:01 +01:00
<TapGestureHandler
onHandlerStateChange={({ nativeEvent }) => {
if (nativeEvent.state === State.ACTIVE) {
callDismiss()
}
}}
>
2020-11-28 17:07:30 +01:00
<Animated.View
style={[styles.overlay, { backgroundColor: theme.backgroundOverlay }]}
2020-11-28 17:07:30 +01:00
>
2021-01-14 22:53:01 +01:00
<PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View
style={[
styles.container,
styleTop,
{
backgroundColor: theme.background,
paddingBottom: insets.bottom || StyleConstants.Spacing.L
}
]}
>
<View
style={[
styles.handle,
{ backgroundColor: theme.primaryOverlay }
]}
/>
{children}
<Button
type='text'
content='取消'
2021-01-24 02:25:43 +01:00
onPress={() => {
analytics('bottomsheet_cancel')
handleDismiss()
}}
2021-01-14 22:53:01 +01:00
style={styles.button}
/>
</Animated.View>
</PanGestureHandler>
2020-11-28 17:07:30 +01:00
</Animated.View>
2021-01-14 22:53:01 +01:00
</TapGestureHandler>
2020-11-28 17:07:30 +01:00
</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
},
button: {
2020-12-26 23:05:17 +01:00
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
2020-11-28 17:07:30 +01:00
}
})
export default BottomSheet