Drawer HOC Component

A drawer animation HOC component

Requirements:

  • react-native-reanimated: animation
  • react-native-root-siblings: root sibling
  • Functional component: require hooks

Drawer HOC

DrawerHOC.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/**
* @author Edward
* @date 2024/06/12
* @description Drawer HOC
* usage: /src/test/drawerTest/DrawerWithExpandable.tsx
*/
import { AntQColors, AntQSizes } from "@/componetsEx/DarkColorUtility"
import { hp } from "@/utils/index"
import React, { useEffect } from "react"
import { ViewStyle } from "react-native"
import Animated, {
interpolate,
interpolateColor,
runOnJS,
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated"

const DrawerHOC = (
WrappedComponent: React.ElementType<{ onClose: (cb: () => void) => void }>,
config: {
duration?: number
styles?: {
mask?: ViewStyle
drawer?: ViewStyle
}
}
) => {
const Drawer = () => {
const DURATION = config.duration || 300
const { mask = {}, drawer = {} } = config.styles || {}
const PAGE_HEIGHT = hp(100)
const backgroundOpacity = useSharedValue(0)
const drawerShow = useSharedValue(0)

const drawerBackground = useAnimatedStyle(() => ({
backgroundColor: interpolateColor(
backgroundOpacity.value,
[0, 1],
["rgba(0,0,0,0)", "rgba(0,0,0,0.4)"]
),
}))

const drawerContent = useAnimatedStyle(() => ({
transform: [
{
translateY: interpolate(drawerShow.value, [0, 1], [PAGE_HEIGHT, 0]),
},
],
}))

const close = (cb?: () => void) => {
drawerShow.value = withTiming(0, { duration: DURATION })
backgroundOpacity.value = withTiming(0, { duration: DURATION }, () => {
if (cb) {
runOnJS(cb)()
}
})
}

const open = () => {
drawerShow.value = withTiming(1, { duration: DURATION })
backgroundOpacity.value = withTiming(1, { duration: DURATION })
}

useEffect(() => {
open()
}, [])

return (
<Animated.View
style={[
{
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
flex: 1,
justifyContent: "flex-end",
},
drawerBackground as any,
mask,
]}
>
<Animated.View
style={[
{
padding: AntQSizes.PAGE_PADDING_DEFAULT,
backgroundColor: AntQColors.MAIN_WHITE,
alignItems: "center",
},
drawer,
drawerContent,
]}
>
<WrappedComponent onClose={close} />
</Animated.View>
</Animated.View>
)
}

return Drawer
}

export default DrawerHOC
Usage.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// final component
CountryPickerDrawer = DrawerHOC(
({ onClose }) => (
<CountryPicker
closeDrawer={() => {
onClose(() => {
if (this.root) {
this.root.destroy()
this.root = null
}
})
}}
onCheck={async (item) => {
const countryCode = item.flag.substring(5).toUpperCase()

this.doStartLoading()

const { code } = await (NetManager.httpService2(
ReqAction.SetContact,
JSON.stringify({ country: countryCode })
) as Promise<{ code: ErrorCode }>)

this.doStopLoading()

if (code === ErrorCode.OK) {
const userInfo = AppModel.instance.AuthState.userInfo
userInfo.country = countryCode
this.setState({ countryCode })
AppModel.instance.AuthState.setUserInfo(userInfo)
this.doShowMsg(LANAGER_STR("common_save_succ"))
}
}}
/>
),
{
duration: 300,
styles: {
drawer: {
backgroundColor: AntQColors.MAIN_WHITE,
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
},
},
}
)

// open drawer
if (this.root === null) {
this.root = new RootSiblingsManager(<this.CountryPickerDrawer />)
}