mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import * as React from 'react'
2import {
3 Dimensions,
4 NativeSyntheticEvent,
5 Platform,
6 StyleProp,
7 View,
8 ViewStyle,
9} from 'react-native'
10import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core'
11
12import {BottomSheetState, BottomSheetViewProps} from './BottomSheet.types'
13import {BottomSheetPortalProvider} from './BottomSheetPortal'
14import {Context as PortalContext} from './BottomSheetPortal'
15
16const screenHeight = Dimensions.get('screen').height
17
18const NativeView: React.ComponentType<
19 BottomSheetViewProps & {
20 ref: React.RefObject<any>
21 style: StyleProp<ViewStyle>
22 }
23> = requireNativeViewManager('BottomSheet')
24
25const NativeModule = requireNativeModule('BottomSheet')
26
27const isIOS15 = Platform.OS === 'ios' && Number(Platform.Version) < 16
28
29export class BottomSheetNativeComponent extends React.Component<
30 BottomSheetViewProps,
31 {
32 open: boolean
33 viewHeight?: number
34 }
35> {
36 ref = React.createRef<any>()
37
38 static contextType = PortalContext
39
40 constructor(props: BottomSheetViewProps) {
41 super(props)
42 this.state = {
43 open: false,
44 }
45 }
46
47 present() {
48 this.setState({open: true})
49 }
50
51 dismiss() {
52 this.ref.current?.dismiss()
53 }
54
55 private onStateChange = (
56 event: NativeSyntheticEvent<{state: BottomSheetState}>,
57 ) => {
58 const {state} = event.nativeEvent
59 const isOpen = state !== 'closed'
60 this.setState({open: isOpen})
61 this.props.onStateChange?.(event)
62 }
63
64 private updateLayout = () => {
65 this.ref.current?.updateLayout()
66 }
67
68 static dismissAll = async () => {
69 await NativeModule.dismissAll()
70 }
71
72 render() {
73 const Portal = this.context as React.ContextType<typeof PortalContext>
74 if (!Portal) {
75 throw new Error(
76 'BottomSheet: You need to wrap your component tree with a <BottomSheetPortalProvider> to use the bottom sheet.',
77 )
78 }
79
80 if (!this.state.open) {
81 return null
82 }
83
84 const {children, backgroundColor, ...rest} = this.props
85 const cornerRadius = rest.cornerRadius ?? 0
86
87 let extraStyles
88 if (isIOS15 && this.state.viewHeight) {
89 const {viewHeight} = this.state
90 if (viewHeight < screenHeight / 2) {
91 extraStyles = {
92 height: viewHeight,
93 marginTop: screenHeight / 2 - viewHeight,
94 borderTopLeftRadius: cornerRadius,
95 borderTopRightRadius: cornerRadius,
96 }
97 }
98 }
99
100 return (
101 <Portal>
102 <NativeView
103 {...rest}
104 onStateChange={this.onStateChange}
105 ref={this.ref}
106 style={{
107 position: 'absolute',
108 height: screenHeight,
109 width: '100%',
110 }}
111 containerBackgroundColor={backgroundColor}>
112 <View
113 style={[
114 {
115 flex: 1,
116 backgroundColor,
117 },
118 Platform.OS === 'android' && {
119 borderTopLeftRadius: cornerRadius,
120 borderTopRightRadius: cornerRadius,
121 },
122 extraStyles,
123 ]}>
124 <View
125 onLayout={e => {
126 const {height} = e.nativeEvent.layout
127 this.setState({viewHeight: height})
128 this.updateLayout()
129 }}>
130 <BottomSheetPortalProvider>{children}</BottomSheetPortalProvider>
131 </View>
132 </View>
133 </NativeView>
134 </Portal>
135 )
136 }
137}