1
1
import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"
2
2
import { useHeaderHeight } from "@react-navigation/elements"
3
3
import { router , Stack , useNavigation } from "expo-router"
4
- import type { FC } from "react"
4
+ import type { FC , PropsWithChildren } from "react"
5
+ import { createContext , useContext , useEffect , useMemo , useState } from "react"
5
6
import type { ScrollViewProps } from "react-native"
6
- import { ScrollView , TouchableOpacity , View } from "react-native"
7
+ import {
8
+ Animated as RNAnimated ,
9
+ StyleSheet ,
10
+ TouchableOpacity ,
11
+ useAnimatedValue ,
12
+ View ,
13
+ } from "react-native"
14
+ import type { ReanimatedScrollEvent } from "react-native-reanimated/lib/typescript/hook/commonTypes"
7
15
import { useSafeAreaInsets } from "react-native-safe-area-context"
8
16
import { useColor } from "react-native-uikit-colors"
9
17
10
18
import { MingcuteLeftLineIcon } from "@/src/icons/mingcute_left_line"
11
19
12
- import { BlurEffectWithBottomBorder } from "./BlurEffect"
20
+ import { AnimatedScrollView } from "./AnimatedComponents"
21
+ import { ThemedBlurView } from "./ThemedBlurView"
13
22
14
- type SafeNavigationScrollViewProps = ScrollViewProps & {
23
+ type SafeNavigationScrollViewProps = Omit < ScrollViewProps , "onScroll" > & {
15
24
withHeaderBlur ?: boolean
16
- }
25
+ onScroll ?: ( e : ReanimatedScrollEvent ) => void
26
+
27
+ // For scroll view content adjustment behavior
28
+ withTopInset ?: boolean
29
+ withBottomInset ?: boolean
30
+ } & PropsWithChildren
31
+ const NavigationContext = createContext < {
32
+ scrollY : RNAnimated . Value
33
+ } | null > ( null )
17
34
18
35
export const SafeNavigationScrollView : FC < SafeNavigationScrollViewProps > = ( {
19
36
children,
20
37
21
38
withHeaderBlur = true ,
39
+ onScroll,
40
+
41
+ withBottomInset = false ,
42
+ withTopInset = false ,
43
+
22
44
...props
23
45
} ) => {
24
46
const insets = useSafeAreaInsets ( )
25
47
const tabBarHeight = useBottomTabBarHeight ( )
26
48
const headerHeight = useHeaderHeight ( )
27
49
50
+ const scrollY = useAnimatedValue ( 0 )
51
+
28
52
return (
29
- < >
53
+ < NavigationContext . Provider value = { useMemo ( ( ) => ( { scrollY } ) , [ scrollY ] ) } >
30
54
{ withHeaderBlur && < NavigationBlurEffectHeader /> }
31
- < ScrollView contentInsetAdjustmentBehavior = "automatic" { ...props } >
32
- < View style = { { height : headerHeight - insets . top } } />
55
+ < AnimatedScrollView
56
+ onScroll = { RNAnimated . event ( [ { nativeEvent : { contentOffset : { y : scrollY } } } ] , {
57
+ useNativeDriver : true ,
58
+ } ) }
59
+ { ...props }
60
+ >
61
+ < View style = { { height : headerHeight - ( withTopInset ? insets . top : 0 ) } } />
33
62
< View > { children } </ View >
34
- < View style = { { height : tabBarHeight - insets . bottom } } />
35
- </ ScrollView >
36
- </ >
63
+ < View style = { { height : tabBarHeight - ( withBottomInset ? insets . bottom : 0 ) } } />
64
+ </ AnimatedScrollView >
65
+ </ NavigationContext . Provider >
37
66
)
38
67
}
39
68
@@ -45,10 +74,35 @@ export const NavigationBlurEffectHeader = (props: NavigationBlurEffectHeaderProp
45
74
46
75
const canBack = useNavigation ( ) . canGoBack ( )
47
76
77
+ const { scrollY } = useContext ( NavigationContext ) !
78
+
79
+ const border = useColor ( "opaqueSeparator" )
80
+
81
+ const [ opacity , setOpacity ] = useState ( 0 )
82
+
83
+ useEffect ( ( ) => {
84
+ const id = scrollY . addListener ( ( { value } ) => {
85
+ setOpacity ( Math . min ( 1 , Math . max ( 0 , Math . min ( 1 , value / 10 ) ) ) )
86
+ } )
87
+
88
+ return ( ) => {
89
+ scrollY . removeListener ( id )
90
+ }
91
+ } , [ scrollY ] )
92
+
48
93
return (
49
94
< Stack . Screen
50
95
options = { {
51
- headerBackground : BlurEffectWithBottomBorder ,
96
+ headerBackground : ( ) => (
97
+ < ThemedBlurView
98
+ style = { {
99
+ ...StyleSheet . absoluteFillObject ,
100
+ opacity,
101
+ borderBottomWidth : StyleSheet . hairlineWidth ,
102
+ borderBottomColor : border ,
103
+ } }
104
+ />
105
+ ) ,
52
106
headerTransparent : true ,
53
107
54
108
headerLeft : canBack
0 commit comments