1- import React , { useMemo , useCallback } from 'react' ;
1+ import React , { useMemo , useCallback , useEffect , useRef } from 'react' ;
2+ import type { LayoutChangeEvent } from 'react-native' ;
23import { useSafeArea } from 'react-native-safe-area-context' ;
34import { AnimatedTabBarView } from './AnimatedTabBarView' ;
45import { useStableCallback } from './utilities' ;
6+ import { useTabBarVisibility } from './hooks' ;
57import type { PresetEnum } from './presets' ;
68import type { AnimatedTabBarProps } from './types' ;
9+ import Animated , {
10+ interpolate ,
11+ Extrapolate ,
12+ useCode ,
13+ cond ,
14+ eq ,
15+ call ,
16+ onChange ,
17+ } from 'react-native-reanimated' ;
18+ import { useValue } from 'react-native-redash' ;
719
820interface Route {
921 name : string ;
@@ -26,24 +38,11 @@ export function AnimatedTabBar<T extends PresetEnum>(
2638 ...rest
2739 } = props ;
2840
29- //#region styles
30- const { bottom : _safeBottomArea } = useSafeArea ( ) ;
31- const safeBottomArea = useMemo (
32- ( ) => overrideSafeAreaInsets ?. bottom ?? _safeBottomArea ,
33- [ overrideSafeAreaInsets , _safeBottomArea ]
34- ) ;
35- const style = useMemo (
36- ( ) => ( {
37- // @ts -ignore
38- ...overrideStyle ,
39- ...{ paddingBottom : safeBottomArea } ,
40- } ) ,
41- [ overrideStyle , safeBottomArea ]
42- ) ;
43- //#endregion
44-
4541 //#region variables
42+ const tabBarContainerRef = useRef < Animated . View > ( null ) ;
4643 const isReactNavigation5 = useMemo ( ( ) => Boolean ( state ) , [ state ] ) ;
44+ const tabBarHeight = useValue < number > ( 0 ) ;
45+
4746 const CommonActions = useMemo ( ( ) => {
4847 if ( isReactNavigation5 ) {
4948 const {
@@ -74,6 +73,21 @@ export function AnimatedTabBar<T extends PresetEnum>(
7473 }
7574 } , [ state , navigation , isReactNavigation5 ] ) ;
7675
76+ const shouldShowTabBar = useMemo ( ( ) => {
77+ /**
78+ * In React Navigation 4 the router view takes care of
79+ * hiding the tab bar.
80+ */
81+ if ( ! isReactNavigation5 ) {
82+ return true ;
83+ }
84+ const route = routes [ navigationIndex ] ;
85+ const { options } = descriptors [ route . key ] ;
86+ return options . tabBarVisible ?? true ;
87+ } , [ isReactNavigation5 , routes , descriptors , navigationIndex ] ) ;
88+
89+ const shouldShowTabBarAnimated = useTabBarVisibility ( shouldShowTabBar ) ;
90+
7791 const getRouteTitle = useCallback (
7892 ( route : Route ) => {
7993 if ( isReactNavigation5 ) {
@@ -113,6 +127,40 @@ export function AnimatedTabBar<T extends PresetEnum>(
113127 } , [ routes , getRouteTitle , getRouteTabConfigs ] ) as any ;
114128 //#endregion
115129
130+ //#region styles
131+ const { bottom : _safeBottomArea } = useSafeArea ( ) ;
132+ const safeBottomArea = useMemo (
133+ ( ) => overrideSafeAreaInsets ?. bottom ?? _safeBottomArea ,
134+ [ overrideSafeAreaInsets , _safeBottomArea ]
135+ ) ;
136+ const style = useMemo (
137+ ( ) => ( {
138+ // @ts -ignore
139+ ...overrideStyle ,
140+ paddingBottom : safeBottomArea ,
141+ } ) ,
142+ [ overrideStyle , safeBottomArea ]
143+ ) ;
144+ const containerStyle = useMemo (
145+ ( ) => ( {
146+ bottom : 0 ,
147+ left : 0 ,
148+ right : 0 ,
149+ transform : [
150+ {
151+ translateY : interpolate ( shouldShowTabBarAnimated , {
152+ inputRange : [ 0 , 1 ] ,
153+ outputRange : [ tabBarHeight , 0 ] ,
154+ extrapolate : Extrapolate . CLAMP ,
155+ } ) ,
156+ } ,
157+ ] ,
158+ } ) ,
159+ // eslint-disable-next-line react-hooks/exhaustive-deps
160+ [ ]
161+ ) ;
162+ //#endregion
163+
116164 //#region callbacks
117165 const handleIndexChange = useStableCallback ( ( index : number ) => {
118166 if ( isReactNavigation5 ) {
@@ -146,17 +194,69 @@ export function AnimatedTabBar<T extends PresetEnum>(
146194 onTabLongPress ( { route : routes [ index ] } ) ;
147195 }
148196 } ) ;
197+ const handleLayout = useCallback (
198+ ( {
199+ nativeEvent : {
200+ layout : { height } ,
201+ } ,
202+ } : LayoutChangeEvent ) => {
203+ tabBarHeight . setValue ( height ) ;
204+ } ,
205+ // eslint-disable-next-line react-hooks/exhaustive-deps
206+ [ ]
207+ ) ;
208+ //#endregion
209+
210+ //#region effects
211+ useCode (
212+ ( ) =>
213+ onChange (
214+ shouldShowTabBarAnimated ,
215+ cond (
216+ eq ( shouldShowTabBarAnimated , 1 ) ,
217+ call ( [ ] , ( ) => {
218+ if ( tabBarContainerRef . current ) {
219+ // @ts -ignore
220+ tabBarContainerRef . current . setNativeProps ( {
221+ style : {
222+ position : 'relative' ,
223+ } ,
224+ } ) ;
225+ }
226+ } )
227+ )
228+ ) ,
229+ [ ]
230+ ) ;
231+ useEffect ( ( ) => {
232+ if ( ! shouldShowTabBar ) {
233+ if ( tabBarContainerRef . current ) {
234+ // @ts -ignore
235+ tabBarContainerRef . current . setNativeProps ( {
236+ style : {
237+ position : 'absolute' ,
238+ } ,
239+ } ) ;
240+ }
241+ }
242+ } , [ shouldShowTabBar ] ) ;
149243 //#endregion
150244
151245 // render
152246 return (
153- < AnimatedTabBarView
154- index = { navigationIndex }
155- onIndexChange = { handleIndexChange }
156- onLongPress = { handleLongPress }
157- tabs = { routesWithTabConfig }
158- style = { style }
159- { ...rest }
160- />
247+ < Animated . View
248+ ref = { tabBarContainerRef }
249+ style = { containerStyle }
250+ onLayout = { handleLayout }
251+ >
252+ < AnimatedTabBarView
253+ index = { navigationIndex }
254+ onIndexChange = { handleIndexChange }
255+ onLongPress = { handleLongPress }
256+ tabs = { routesWithTabConfig }
257+ style = { style }
258+ { ...rest }
259+ />
260+ </ Animated . View >
161261 ) ;
162262}
0 commit comments