@momo-kits/foundation 0.161.2-beta.7 → 0.161.2-reanimated.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Application/Components/BackgroundImageView.tsx +6 -22
- package/Application/Components/HeaderAnimated.tsx +29 -32
- package/Application/Components/HeaderBackground.tsx +26 -20
- package/Application/Components/HeaderExtendHeader.tsx +94 -114
- package/Application/Components/HeaderTitle.tsx +10 -39
- package/Application/Components/SearchHeader.tsx +21 -16
- package/Application/types.ts +4 -4
- package/Application/utils.tsx +3 -4
- package/Layout/FloatingButton.tsx +59 -59
- package/Layout/Screen.tsx +57 -61
- package/package.json +1 -1
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import React, { FC, useContext } from 'react';
|
|
2
|
-
import { Platform, StyleSheet } from 'react-native';
|
|
3
|
-
import Animated, {
|
|
4
|
-
Extrapolation,
|
|
5
|
-
interpolate,
|
|
6
|
-
useAnimatedStyle,
|
|
7
|
-
type SharedValue,
|
|
8
|
-
} from 'react-native-reanimated';
|
|
2
|
+
import { Animated, Platform, StyleSheet } from 'react-native';
|
|
9
3
|
import { Image } from '../../Image';
|
|
10
4
|
import { Colors } from '../../Consts';
|
|
11
5
|
import { ApplicationContext } from '../../Context';
|
|
@@ -14,26 +8,16 @@ type BackgroundImageViewProps = {
|
|
|
14
8
|
useShadowHeader: boolean;
|
|
15
9
|
headerBackground?: string;
|
|
16
10
|
heightHeader: number | `${number}%`;
|
|
17
|
-
|
|
11
|
+
opacityBackground: Animated.AnimatedInterpolation<any>;
|
|
18
12
|
};
|
|
19
13
|
|
|
20
14
|
const BackgroundImageView: FC<BackgroundImageViewProps> = ({
|
|
21
15
|
useShadowHeader = true,
|
|
22
16
|
headerBackground = null,
|
|
23
17
|
heightHeader,
|
|
24
|
-
|
|
18
|
+
opacityBackground,
|
|
25
19
|
}) => {
|
|
26
20
|
const { theme } = useContext(ApplicationContext);
|
|
27
|
-
|
|
28
|
-
const opacityStyle = useAnimatedStyle(() => ({
|
|
29
|
-
opacity: interpolate(
|
|
30
|
-
animatedValue.value,
|
|
31
|
-
[0, 52],
|
|
32
|
-
[0, 1],
|
|
33
|
-
Extrapolation.CLAMP,
|
|
34
|
-
),
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
21
|
if (Platform.OS === 'android') {
|
|
38
22
|
return (
|
|
39
23
|
<Animated.View
|
|
@@ -43,8 +27,8 @@ const BackgroundImageView: FC<BackgroundImageViewProps> = ({
|
|
|
43
27
|
{
|
|
44
28
|
height: heightHeader,
|
|
45
29
|
backgroundColor: theme.colors.background.surface,
|
|
30
|
+
opacity: opacityBackground,
|
|
46
31
|
},
|
|
47
|
-
opacityStyle,
|
|
48
32
|
]}
|
|
49
33
|
>
|
|
50
34
|
{headerBackground && (
|
|
@@ -67,8 +51,8 @@ const BackgroundImageView: FC<BackgroundImageViewProps> = ({
|
|
|
67
51
|
{
|
|
68
52
|
height: heightHeader,
|
|
69
53
|
backgroundColor: theme.colors.background.surface,
|
|
54
|
+
opacity: opacityBackground,
|
|
70
55
|
},
|
|
71
|
-
opacityStyle,
|
|
72
56
|
]}
|
|
73
57
|
/>
|
|
74
58
|
<Animated.View
|
|
@@ -76,8 +60,8 @@ const BackgroundImageView: FC<BackgroundImageViewProps> = ({
|
|
|
76
60
|
styles.hidden,
|
|
77
61
|
{
|
|
78
62
|
height: heightHeader,
|
|
63
|
+
opacity: opacityBackground,
|
|
79
64
|
},
|
|
80
|
-
opacityStyle,
|
|
81
65
|
]}
|
|
82
66
|
>
|
|
83
67
|
{headerBackground && (
|
|
@@ -1,49 +1,46 @@
|
|
|
1
|
-
import { StyleSheet } from 'react-native';
|
|
2
|
-
import Animated, {
|
|
3
|
-
Extrapolation,
|
|
4
|
-
interpolate,
|
|
5
|
-
useAnimatedStyle,
|
|
6
|
-
} from 'react-native-reanimated';
|
|
1
|
+
import { Animated, StyleSheet } from 'react-native';
|
|
7
2
|
import { type HeaderAnimatedProps } from '../types';
|
|
8
3
|
import React from 'react';
|
|
9
4
|
import { Image } from '../../Image';
|
|
10
5
|
import { Colors } from '../../Consts';
|
|
11
6
|
|
|
7
|
+
/**
|
|
8
|
+
* default header banner for header animated
|
|
9
|
+
*/
|
|
12
10
|
const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
|
|
13
11
|
animatedValue,
|
|
14
12
|
image,
|
|
15
13
|
useScale = true,
|
|
16
14
|
children,
|
|
17
15
|
}) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
animatedValue.value,
|
|
35
|
-
[-300, 0],
|
|
36
|
-
[-80, -2],
|
|
37
|
-
Extrapolation.CLAMP,
|
|
38
|
-
);
|
|
39
|
-
return {
|
|
40
|
-
opacity,
|
|
41
|
-
transform: [{ scale }, { translateY }],
|
|
42
|
-
};
|
|
16
|
+
const scale = useScale
|
|
17
|
+
? animatedValue.interpolate({
|
|
18
|
+
inputRange: [-300, 0, 300],
|
|
19
|
+
outputRange: [4, 1, 1],
|
|
20
|
+
extrapolate: 'clamp',
|
|
21
|
+
})
|
|
22
|
+
: 1;
|
|
23
|
+
const opacity = animatedValue.interpolate({
|
|
24
|
+
inputRange: [0, 150, 300],
|
|
25
|
+
outputRange: [1, 0.5, 0],
|
|
26
|
+
extrapolate: 'clamp',
|
|
27
|
+
});
|
|
28
|
+
const translateY = animatedValue.interpolate({
|
|
29
|
+
inputRange: [-300, 0],
|
|
30
|
+
outputRange: [-80, -2],
|
|
31
|
+
extrapolate: 'clamp',
|
|
43
32
|
});
|
|
44
33
|
|
|
45
34
|
return (
|
|
46
|
-
<Animated.View
|
|
35
|
+
<Animated.View
|
|
36
|
+
style={[
|
|
37
|
+
styles.container,
|
|
38
|
+
{
|
|
39
|
+
opacity: opacity,
|
|
40
|
+
transform: [{ scale }, { translateY }],
|
|
41
|
+
},
|
|
42
|
+
]}
|
|
43
|
+
>
|
|
47
44
|
{children ?? (
|
|
48
45
|
<Image
|
|
49
46
|
source={{
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import LinearGradient from 'react-native-linear-gradient';
|
|
2
|
-
import { StyleSheet, View } from 'react-native';
|
|
2
|
+
import { Animated, StyleSheet, View } from 'react-native';
|
|
3
3
|
import React, { useContext } from 'react';
|
|
4
|
-
import Animated, { useSharedValue } from 'react-native-reanimated';
|
|
5
4
|
import { HeaderBackgroundProps } from '../types';
|
|
6
5
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
7
6
|
import { Colors, Styles } from '../../Consts';
|
|
8
7
|
import { Image } from '../../Image';
|
|
9
8
|
import BackgroundImageView from './BackgroundImageView';
|
|
10
9
|
|
|
10
|
+
const LinearGradientAnimated = Animated.createAnimatedComponent(LinearGradient);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* header background for default
|
|
14
|
+
*/
|
|
11
15
|
const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
12
|
-
animatedValue,
|
|
16
|
+
animatedValue = new Animated.Value(0),
|
|
13
17
|
useGradient = true,
|
|
14
18
|
useShadowHeader = true,
|
|
15
19
|
gradientColor: customGradientColor,
|
|
@@ -19,35 +23,37 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
|
19
23
|
const context = useContext<any>(MiniAppContext);
|
|
20
24
|
const gradientColor = customGradientColor ?? theme.colors.gradient;
|
|
21
25
|
const headerImage = headerBackground ?? theme.assets?.headerBackground;
|
|
22
|
-
const fallback = useSharedValue(0);
|
|
23
|
-
const sv = animatedValue ?? fallback;
|
|
24
26
|
|
|
25
27
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
26
28
|
|
|
29
|
+
const opacityBackground = animatedValue?.interpolate({
|
|
30
|
+
inputRange: [0, 52],
|
|
31
|
+
outputRange: [0, 1],
|
|
32
|
+
extrapolate: 'clamp',
|
|
33
|
+
});
|
|
34
|
+
|
|
27
35
|
return (
|
|
28
36
|
<View style={[Styles.flex, showBaseLineDebug && styles.debugBaseLine]}>
|
|
29
37
|
<BackgroundImageView
|
|
30
38
|
useShadowHeader={useShadowHeader}
|
|
31
39
|
heightHeader={'100%'}
|
|
32
|
-
|
|
40
|
+
opacityBackground={opacityBackground}
|
|
33
41
|
headerBackground={headerImage}
|
|
34
42
|
/>
|
|
35
43
|
<View style={styles.gradientContainer}>
|
|
36
44
|
{useGradient && !!gradientColor && (
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
</LinearGradient>
|
|
50
|
-
</Animated.View>
|
|
45
|
+
<LinearGradientAnimated
|
|
46
|
+
colors={[gradientColor, gradientColor + '00']}
|
|
47
|
+
style={styles.extendedHeader}
|
|
48
|
+
>
|
|
49
|
+
{!!headerImage && (
|
|
50
|
+
<Image
|
|
51
|
+
style={styles.headerBackground}
|
|
52
|
+
source={{ uri: headerImage }}
|
|
53
|
+
loading={false}
|
|
54
|
+
/>
|
|
55
|
+
)}
|
|
56
|
+
</LinearGradientAnimated>
|
|
51
57
|
)}
|
|
52
58
|
</View>
|
|
53
59
|
</View>
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import React, { Ref, useContext } from 'react';
|
|
1
|
+
import React, { Ref, useContext, useEffect, useRef } from 'react';
|
|
2
2
|
import LinearGradient from 'react-native-linear-gradient';
|
|
3
|
-
import { Dimensions, Platform, StyleSheet, View } from 'react-native';
|
|
4
|
-
import Animated, {
|
|
5
|
-
Extrapolation,
|
|
6
|
-
interpolate,
|
|
7
|
-
useAnimatedStyle,
|
|
8
|
-
useSharedValue,
|
|
9
|
-
type SharedValue,
|
|
10
|
-
} from 'react-native-reanimated';
|
|
11
3
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
4
|
+
import { Animated, Dimensions, Platform, StyleSheet, View } from 'react-native';
|
|
12
5
|
import { HeaderType } from '../../Layout/types';
|
|
13
6
|
import { InputRef, InputSearch } from '../../Input';
|
|
14
7
|
import Navigation from '../Navigation';
|
|
@@ -21,10 +14,15 @@ const SCREEN_PADDING = 12;
|
|
|
21
14
|
const BACK_WIDTH = 28;
|
|
22
15
|
|
|
23
16
|
const { width: screenWidth } = Dimensions.get('window');
|
|
17
|
+
const LinearGradientAnimated = Animated.createAnimatedComponent(LinearGradient);
|
|
24
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Header extended with background image
|
|
21
|
+
* @constructor
|
|
22
|
+
*/
|
|
25
23
|
const HeaderExtendHeader: React.FC<{
|
|
26
24
|
headerType?: HeaderType;
|
|
27
|
-
animatedValue:
|
|
25
|
+
animatedValue: Animated.Value;
|
|
28
26
|
heightHeader: number;
|
|
29
27
|
headerRightWidth: number;
|
|
30
28
|
inputSearchProps?: SearchHeaderProps;
|
|
@@ -40,132 +38,118 @@ const HeaderExtendHeader: React.FC<{
|
|
|
40
38
|
headerRightWidth = 73,
|
|
41
39
|
inputSearchProps,
|
|
42
40
|
inputSearchRef,
|
|
43
|
-
useShadowHeader
|
|
41
|
+
useShadowHeader = true,
|
|
44
42
|
gradientColor: customGradientColor,
|
|
45
43
|
headerBackground: customBackground,
|
|
46
44
|
}) => {
|
|
47
45
|
const { theme } = useContext(ApplicationContext);
|
|
48
46
|
const context = useContext<any>(MiniAppContext);
|
|
49
|
-
const
|
|
50
|
-
const sv = animatedValue ?? fallback;
|
|
47
|
+
const animated = useRef(new Animated.Value(0));
|
|
51
48
|
const gradientColor = customGradientColor ?? theme.colors.gradient;
|
|
52
49
|
const headerBackground = customBackground ?? theme.assets?.headerBackground;
|
|
53
50
|
const leftPosition = inputSearchProps?.leftPosition || BACK_WIDTH + 20;
|
|
54
51
|
|
|
55
|
-
let useShadowHeader = useShadowHeaderProp;
|
|
56
|
-
if (inputSearchProps && Platform.OS === 'android') {
|
|
57
|
-
useShadowHeader = false;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
52
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
61
53
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
54
|
+
const opacityBackground = animatedValue?.interpolate({
|
|
55
|
+
inputRange: [0, 52],
|
|
56
|
+
outputRange: [0, 1],
|
|
57
|
+
extrapolate: 'clamp',
|
|
58
|
+
});
|
|
59
|
+
const opacityGradient = animatedValue?.interpolate({
|
|
60
|
+
inputRange: [0, 52],
|
|
61
|
+
outputRange: [1, 0],
|
|
62
|
+
extrapolate: 'clamp',
|
|
63
|
+
});
|
|
70
64
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const listener = animatedValue.addListener(({ value }) => {
|
|
67
|
+
animated.current.setValue(value);
|
|
68
|
+
});
|
|
69
|
+
return () => {
|
|
70
|
+
animatedValue?.removeListener(listener);
|
|
71
|
+
};
|
|
72
|
+
}, [animatedValue]);
|
|
74
73
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
[0, 100],
|
|
81
|
-
[SCREEN_PADDING, leftPosition],
|
|
82
|
-
Extrapolation.CLAMP,
|
|
83
|
-
),
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
width: interpolate(
|
|
87
|
-
sv.value,
|
|
88
|
-
[0, 100],
|
|
89
|
-
[
|
|
90
|
-
screenWidth - SCREEN_PADDING * 2,
|
|
91
|
-
screenWidth - leftPosition - 12 - headerRightWidth,
|
|
92
|
-
],
|
|
93
|
-
Extrapolation.CLAMP,
|
|
94
|
-
),
|
|
95
|
-
}));
|
|
74
|
+
const height = animated.current.interpolate({
|
|
75
|
+
inputRange: [0, 100],
|
|
76
|
+
outputRange: [heightHeader + 52, heightHeader],
|
|
77
|
+
extrapolate: 'clamp',
|
|
78
|
+
});
|
|
96
79
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
: theme.colors.background.default };
|
|
80
|
+
const translateX = animated.current.interpolate({
|
|
81
|
+
inputRange: [0, 100],
|
|
82
|
+
outputRange: [SCREEN_PADDING, leftPosition],
|
|
83
|
+
extrapolate: 'clamp',
|
|
102
84
|
});
|
|
103
85
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
})
|
|
86
|
+
const backgroundColor = animated.current.interpolate({
|
|
87
|
+
inputRange: [0, 100],
|
|
88
|
+
outputRange: [
|
|
89
|
+
theme.colors.background.surface,
|
|
90
|
+
theme.colors.background.default,
|
|
91
|
+
],
|
|
92
|
+
extrapolate: 'clamp',
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (inputSearchProps && Platform.OS === 'android') {
|
|
96
|
+
useShadowHeader = false;
|
|
97
|
+
}
|
|
112
98
|
|
|
113
99
|
if (inputSearchProps) {
|
|
114
100
|
return (
|
|
115
101
|
<View style={[{ zIndex: 0 }, showBaseLineDebug && styles.debugBaseLine]}>
|
|
116
|
-
<Animated.View style={
|
|
102
|
+
<Animated.View style={{ height: height }} />
|
|
117
103
|
<BackgroundImageView
|
|
118
104
|
useShadowHeader={useShadowHeader}
|
|
119
105
|
heightHeader={heightHeader}
|
|
120
|
-
|
|
106
|
+
opacityBackground={opacityBackground}
|
|
121
107
|
headerBackground={headerBackground}
|
|
122
108
|
/>
|
|
123
109
|
<Animated.View
|
|
124
110
|
style={[
|
|
125
111
|
styles.headerBox,
|
|
126
|
-
headerType === 'extended'
|
|
127
|
-
? heightStyle
|
|
128
|
-
: { height: heightHeader },
|
|
112
|
+
{ height: headerType === 'extended' ? height : heightHeader },
|
|
129
113
|
]}
|
|
130
114
|
>
|
|
131
115
|
{!!gradientColor && (
|
|
132
|
-
<
|
|
133
|
-
|
|
116
|
+
<LinearGradientAnimated
|
|
117
|
+
colors={[gradientColor, gradientColor + '00']}
|
|
118
|
+
style={[styles.extendedHeader, { opacity: opacityGradient }]}
|
|
134
119
|
>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
loading={false}
|
|
144
|
-
/>
|
|
145
|
-
)}
|
|
146
|
-
</LinearGradient>
|
|
147
|
-
</Animated.View>
|
|
120
|
+
{!!theme.assets?.headerBackground && (
|
|
121
|
+
<Image
|
|
122
|
+
style={styles.headerBackground}
|
|
123
|
+
source={{ uri: theme.assets?.headerBackground }}
|
|
124
|
+
loading={false}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
</LinearGradientAnimated>
|
|
148
128
|
)}
|
|
149
129
|
</Animated.View>
|
|
150
130
|
<Animated.View
|
|
151
|
-
style={
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
heightStyle,
|
|
158
|
-
]}
|
|
131
|
+
style={{
|
|
132
|
+
justifyContent: 'flex-end',
|
|
133
|
+
height,
|
|
134
|
+
position: 'absolute',
|
|
135
|
+
zIndex: 2,
|
|
136
|
+
}}
|
|
159
137
|
>
|
|
160
138
|
<Animated.View
|
|
161
|
-
style={
|
|
139
|
+
style={{
|
|
140
|
+
transform: [{ translateX }],
|
|
141
|
+
marginVertical: Spacing.S,
|
|
142
|
+
width: animated.current.interpolate({
|
|
143
|
+
inputRange: [0, 100],
|
|
144
|
+
outputRange: [
|
|
145
|
+
screenWidth - SCREEN_PADDING * 2,
|
|
146
|
+
screenWidth - leftPosition - 12 - headerRightWidth,
|
|
147
|
+
],
|
|
148
|
+
extrapolate: 'clamp',
|
|
149
|
+
}),
|
|
150
|
+
}}
|
|
162
151
|
>
|
|
163
|
-
<Animated.View
|
|
164
|
-
style={[
|
|
165
|
-
{ borderRadius: Radius.XL },
|
|
166
|
-
searchBackgroundStyle,
|
|
167
|
-
]}
|
|
168
|
-
>
|
|
152
|
+
<Animated.View style={{ backgroundColor, borderRadius: Radius.XL }}>
|
|
169
153
|
<InputSearch
|
|
170
154
|
{...inputSearchProps}
|
|
171
155
|
ref={inputSearchRef}
|
|
@@ -185,26 +169,22 @@ const HeaderExtendHeader: React.FC<{
|
|
|
185
169
|
<BackgroundImageView
|
|
186
170
|
useShadowHeader={useShadowHeader}
|
|
187
171
|
heightHeader={heightHeader}
|
|
188
|
-
|
|
172
|
+
opacityBackground={opacityBackground}
|
|
189
173
|
headerBackground={headerBackground}
|
|
190
174
|
/>
|
|
191
175
|
{!!gradientColor && (
|
|
192
|
-
<
|
|
193
|
-
|
|
176
|
+
<LinearGradientAnimated
|
|
177
|
+
colors={[gradientColor, gradientColor + '00']}
|
|
178
|
+
style={[styles.extendedHeader, { opacity: opacityGradient }]}
|
|
194
179
|
>
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
loading={false}
|
|
204
|
-
/>
|
|
205
|
-
)}
|
|
206
|
-
</LinearGradient>
|
|
207
|
-
</Animated.View>
|
|
180
|
+
{!!headerBackground && (
|
|
181
|
+
<Image
|
|
182
|
+
style={styles.headerBackground}
|
|
183
|
+
source={{ uri: headerBackground }}
|
|
184
|
+
loading={false}
|
|
185
|
+
/>
|
|
186
|
+
)}
|
|
187
|
+
</LinearGradientAnimated>
|
|
208
188
|
)}
|
|
209
189
|
<View style={{ height: heightHeader }} />
|
|
210
190
|
</View>
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
Animated,
|
|
3
4
|
Dimensions,
|
|
4
5
|
StyleSheet,
|
|
5
6
|
TouchableOpacity,
|
|
6
7
|
View,
|
|
7
8
|
} from 'react-native';
|
|
8
|
-
import Animated, {
|
|
9
|
-
Extrapolation,
|
|
10
|
-
interpolate,
|
|
11
|
-
useAnimatedStyle,
|
|
12
|
-
type SharedValue,
|
|
13
|
-
} from 'react-native-reanimated';
|
|
14
9
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
15
10
|
import { exportFontFamily, Text, useScaleSize } from '../../Text';
|
|
16
11
|
import { Colors, Radius, Spacing, Styles } from '../../Consts';
|
|
@@ -23,45 +18,21 @@ import { Image } from '../../Image';
|
|
|
23
18
|
import { Icon } from '../../Icon';
|
|
24
19
|
import { Skeleton } from '../../Skeleton';
|
|
25
20
|
|
|
26
|
-
type HeaderTitleInterpolate = {
|
|
27
|
-
inputRange: number[];
|
|
28
|
-
outputRange: number[];
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type HeaderTitleExtraProps = {
|
|
32
|
-
animatedValue?: SharedValue<number>;
|
|
33
|
-
interpolate?: HeaderTitleInterpolate;
|
|
34
|
-
tintColor?: string;
|
|
35
|
-
children?: React.ReactNode;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
21
|
/**
|
|
39
22
|
* default header title used for nav
|
|
40
23
|
*/
|
|
41
|
-
const HeaderTitle: React.FC<
|
|
24
|
+
const HeaderTitle: React.FC<any> = props => {
|
|
42
25
|
const context = useContext<any>(MiniAppContext);
|
|
43
26
|
|
|
44
27
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
45
28
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (!animatedValue) {
|
|
54
|
-
return { opacity: 1 };
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
opacity: interpolate(
|
|
58
|
-
animatedValue.value,
|
|
59
|
-
interpolateConfig.inputRange,
|
|
60
|
-
interpolateConfig.outputRange,
|
|
61
|
-
Extrapolation.CLAMP,
|
|
62
|
-
),
|
|
63
|
-
};
|
|
64
|
-
});
|
|
29
|
+
const opacity = props.animatedValue?.interpolate(
|
|
30
|
+
props.interpolate ?? {
|
|
31
|
+
inputRange: [0, 200],
|
|
32
|
+
outputRange: [0, 1],
|
|
33
|
+
extrapolate: 'clamp',
|
|
34
|
+
},
|
|
35
|
+
);
|
|
65
36
|
|
|
66
37
|
return (
|
|
67
38
|
<View
|
|
@@ -86,9 +57,9 @@ const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = pr
|
|
|
86
57
|
},
|
|
87
58
|
{
|
|
88
59
|
fontFamily: exportFontFamily('bold', 'action_xs_bold'),
|
|
60
|
+
opacity,
|
|
89
61
|
color: props.tintColor,
|
|
90
62
|
},
|
|
91
|
-
animatedStyle,
|
|
92
63
|
]}
|
|
93
64
|
numberOfLines={1}
|
|
94
65
|
/>
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import { Colors, Radius, Spacing, Styles } from '../../Consts';
|
|
2
2
|
import { InputRef, InputSearch } from '../../Input';
|
|
3
3
|
import {
|
|
4
|
+
Animated,
|
|
4
5
|
Dimensions,
|
|
5
6
|
StyleSheet,
|
|
6
7
|
TouchableOpacity,
|
|
7
8
|
View,
|
|
8
9
|
} from 'react-native';
|
|
9
|
-
import
|
|
10
|
-
useAnimatedStyle,
|
|
11
|
-
useSharedValue,
|
|
12
|
-
} from 'react-native-reanimated';
|
|
13
|
-
import React, { useContext } from 'react';
|
|
10
|
+
import React, { useContext, useEffect, useRef } from 'react';
|
|
14
11
|
import { SearchHeaderProps } from '../types';
|
|
15
12
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
16
13
|
import { Text } from '../../Text';
|
|
@@ -31,18 +28,28 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
31
28
|
const BACK_WIDTH = 28;
|
|
32
29
|
const { width: screenWidth } = Dimensions.get('window');
|
|
33
30
|
|
|
34
|
-
const
|
|
35
|
-
const sv = animatedValue ?? fallback;
|
|
31
|
+
const animated = useRef(new Animated.Value(0));
|
|
36
32
|
const leftPosition = props?.leftPosition ?? BACK_WIDTH + 20;
|
|
37
33
|
const headerRightWidth = props?.headerRightWidth ?? 73;
|
|
38
34
|
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
const listener = animatedValue?.addListener(({ value }) => {
|
|
37
|
+
animated.current.setValue(value);
|
|
38
|
+
});
|
|
39
|
+
return () => {
|
|
40
|
+
if (listener) {
|
|
41
|
+
animatedValue?.removeListener(listener);
|
|
42
|
+
}
|
|
45
43
|
};
|
|
44
|
+
}, [animatedValue]);
|
|
45
|
+
|
|
46
|
+
const backgroundColor = animated.current?.interpolate({
|
|
47
|
+
inputRange: [0, 100],
|
|
48
|
+
outputRange: [
|
|
49
|
+
theme.colors.background.surface,
|
|
50
|
+
theme.colors.background.default,
|
|
51
|
+
],
|
|
52
|
+
extrapolate: 'clamp',
|
|
46
53
|
});
|
|
47
54
|
|
|
48
55
|
const goBack = () => {
|
|
@@ -84,9 +91,7 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
84
91
|
},
|
|
85
92
|
]}
|
|
86
93
|
>
|
|
87
|
-
<Animated.View
|
|
88
|
-
style={[{ borderRadius: Radius.XL }, backgroundStyle]}
|
|
89
|
-
>
|
|
94
|
+
<Animated.View style={{ backgroundColor, borderRadius: Radius.XL }}>
|
|
90
95
|
<InputSearch
|
|
91
96
|
ref={ref}
|
|
92
97
|
{...props}
|
package/Application/types.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { EventArg } from '@react-navigation/core';
|
|
|
2
2
|
import { StackNavigationOptions } from '@react-navigation/stack';
|
|
3
3
|
import React, { ReactNode } from 'react';
|
|
4
4
|
import {
|
|
5
|
+
Animated,
|
|
5
6
|
TouchableOpacityProps,
|
|
6
7
|
ViewProps,
|
|
7
8
|
ViewStyle,
|
|
8
9
|
} from 'react-native';
|
|
9
|
-
import type { SharedValue } from 'react-native-reanimated';
|
|
10
10
|
import { PopupNotifyProps } from '../Popup/types';
|
|
11
11
|
import { InputRef, InputSearchProps } from '../Input';
|
|
12
12
|
import Navigation from './Navigation';
|
|
@@ -258,7 +258,7 @@ export interface HeaderBackProps extends NavigationButtonProps {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
export type HeaderBackgroundProps = {
|
|
261
|
-
animatedValue?:
|
|
261
|
+
animatedValue?: Animated.Value;
|
|
262
262
|
useGradient?: boolean;
|
|
263
263
|
useShadowHeader?: boolean;
|
|
264
264
|
backgroundColor?: string;
|
|
@@ -298,7 +298,7 @@ export type TitleJourneyProps = {
|
|
|
298
298
|
};
|
|
299
299
|
|
|
300
300
|
export interface HeaderAnimatedProps extends ViewProps {
|
|
301
|
-
animatedValue:
|
|
301
|
+
animatedValue: Animated.Value;
|
|
302
302
|
image: string;
|
|
303
303
|
useScale?: boolean;
|
|
304
304
|
}
|
|
@@ -342,7 +342,7 @@ export type AnimatedHeader = {
|
|
|
342
342
|
|
|
343
343
|
export interface SearchHeaderProps extends InputSearchProps {
|
|
344
344
|
ref?: React.RefObject<InputRef>;
|
|
345
|
-
animatedValue?:
|
|
345
|
+
animatedValue?: Animated.Value;
|
|
346
346
|
headerRightWidth?: 0 | 74 | 110 | number;
|
|
347
347
|
leftPosition?: 12 | 48 | number;
|
|
348
348
|
renderButtons?: () => ReactNode;
|
package/Application/utils.tsx
CHANGED
|
@@ -5,8 +5,7 @@ import {
|
|
|
5
5
|
} from '@react-navigation/stack';
|
|
6
6
|
import type { HeaderTitleProps, NavigationOptions } from './types';
|
|
7
7
|
import { Colors, Spacing } from '../Consts';
|
|
8
|
-
import { AppState, Platform } from 'react-native';
|
|
9
|
-
import type { SharedValue } from 'react-native-reanimated';
|
|
8
|
+
import { Animated, AppState, Platform } from 'react-native';
|
|
10
9
|
import {
|
|
11
10
|
MiniAppContext,
|
|
12
11
|
ScreenContext,
|
|
@@ -61,7 +60,7 @@ const getModalOptions = (): StackNavigationOptions => {
|
|
|
61
60
|
*/
|
|
62
61
|
const getOptions = (
|
|
63
62
|
params: NavigationOptions,
|
|
64
|
-
animatedValue?:
|
|
63
|
+
animatedValue?: Animated.Value,
|
|
65
64
|
) => {
|
|
66
65
|
let options: StackNavigationOptions = {};
|
|
67
66
|
|
|
@@ -132,7 +131,7 @@ const getOptions = (
|
|
|
132
131
|
|
|
133
132
|
const exportHeaderTitle = (
|
|
134
133
|
params: NavigationOptions,
|
|
135
|
-
animatedValue?:
|
|
134
|
+
animatedValue?: Animated.Value,
|
|
136
135
|
): StackNavigationOptions => {
|
|
137
136
|
if (typeof params.headerTitle === 'object') {
|
|
138
137
|
return {
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useState } from 'react';
|
|
2
1
|
import {
|
|
2
|
+
default as React,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import {
|
|
9
|
+
Animated,
|
|
3
10
|
LayoutChangeEvent,
|
|
4
11
|
StyleSheet,
|
|
5
12
|
TouchableOpacity,
|
|
6
13
|
View,
|
|
7
14
|
} from 'react-native';
|
|
8
|
-
import Animated, {
|
|
9
|
-
useAnimatedReaction,
|
|
10
|
-
useAnimatedStyle,
|
|
11
|
-
useSharedValue,
|
|
12
|
-
withTiming,
|
|
13
|
-
runOnJS,
|
|
14
|
-
type SharedValue,
|
|
15
|
-
} from 'react-native-reanimated';
|
|
16
15
|
import { ApplicationContext } from '../Context';
|
|
17
16
|
import { Icon } from '../Icon';
|
|
18
17
|
import { useScaleSize } from '../Text';
|
|
@@ -24,7 +23,7 @@ export interface FloatingButtonProps {
|
|
|
24
23
|
icon?: string;
|
|
25
24
|
iconColor?: string;
|
|
26
25
|
size?: 'small' | 'large';
|
|
27
|
-
animatedValue?:
|
|
26
|
+
animatedValue?: Animated.Value;
|
|
28
27
|
bottom?: number;
|
|
29
28
|
renderComponent?: () => React.ReactNode;
|
|
30
29
|
}
|
|
@@ -43,67 +42,68 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
43
42
|
const { theme } = useContext(ApplicationContext);
|
|
44
43
|
const scaledFontSize = useScaleSize(16);
|
|
45
44
|
const scaledLineHeight = useScaleSize(22);
|
|
46
|
-
const maxWidth =
|
|
45
|
+
const maxWidth = useRef(0);
|
|
47
46
|
const minWidth = size === 'small' ? 36 : 48;
|
|
48
|
-
const opacityAnimated =
|
|
49
|
-
const widthAnimated =
|
|
50
|
-
const lastOffset =
|
|
51
|
-
const lastDirection =
|
|
52
|
-
const [showText, setShowText] = useState(true);
|
|
47
|
+
const [opacityAnimated] = useState(new Animated.Value(0)); // Initial opacity set to 0
|
|
48
|
+
const [widthAnimated, setWidthAnimated] = useState<Animated.Value>();
|
|
49
|
+
const lastOffset = useRef(0);
|
|
50
|
+
const lastDirection = useRef<string>(null);
|
|
51
|
+
const [showText, setShowText] = React.useState(true);
|
|
53
52
|
|
|
54
53
|
useEffect(() => {
|
|
55
54
|
if (!label) return;
|
|
56
|
-
opacityAnimated.value = withTiming(1, { duration: 100 });
|
|
57
|
-
}, [label, opacityAnimated]);
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
Animated.timing(opacityAnimated, {
|
|
57
|
+
toValue: 1,
|
|
58
|
+
duration: 100,
|
|
59
|
+
useNativeDriver: true,
|
|
60
|
+
}).start();
|
|
61
|
+
|
|
62
|
+
const listener = animatedValue?.addListener(({ value }) => {
|
|
63
|
+
if (value !== lastOffset.current && value > 0) {
|
|
64
|
+
const direction = value > lastOffset.current ? 'down' : 'up';
|
|
65
|
+
lastOffset.current = value;
|
|
66
|
+
if (lastDirection.current !== direction) {
|
|
67
|
+
lastDirection.current = direction;
|
|
69
68
|
if (direction === 'down') {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
69
|
+
Animated.timing(opacityAnimated, {
|
|
70
|
+
toValue: 0,
|
|
71
|
+
duration: 100,
|
|
72
|
+
useNativeDriver: true,
|
|
73
|
+
}).start();
|
|
74
|
+
Animated.timing(widthAnimated!, {
|
|
75
|
+
toValue: minWidth,
|
|
76
|
+
duration: 100,
|
|
77
|
+
useNativeDriver: false,
|
|
78
|
+
}).start(() => setShowText(false));
|
|
78
79
|
} else {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
Animated.timing(opacityAnimated, {
|
|
81
|
+
toValue: 1,
|
|
82
|
+
duration: 100,
|
|
83
|
+
useNativeDriver: true,
|
|
84
|
+
}).start();
|
|
85
|
+
Animated.timing(widthAnimated!, {
|
|
86
|
+
toValue: maxWidth.current,
|
|
87
|
+
duration: 100,
|
|
88
|
+
useNativeDriver: false,
|
|
89
|
+
}).start(() => setShowText(true));
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
|
-
}
|
|
91
|
-
[label, animatedValue, minWidth],
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const containerStyle = useAnimatedStyle(() => ({
|
|
95
|
-
width: widthAnimated.value ?? undefined,
|
|
96
|
-
}));
|
|
93
|
+
});
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
return () => {
|
|
96
|
+
if (listener) {
|
|
97
|
+
animatedValue?.removeListener(listener);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}, [animatedValue, label, minWidth, opacityAnimated, widthAnimated]);
|
|
101
101
|
|
|
102
102
|
const handleLayout = (event: LayoutChangeEvent) => {
|
|
103
103
|
const layout = event.nativeEvent.layout;
|
|
104
|
-
if (widthAnimated
|
|
105
|
-
maxWidth.
|
|
106
|
-
|
|
104
|
+
if (widthAnimated) return;
|
|
105
|
+
maxWidth.current = layout.width;
|
|
106
|
+
setWidthAnimated(new Animated.Value(layout.width));
|
|
107
107
|
};
|
|
108
108
|
|
|
109
109
|
if (renderComponent) {
|
|
@@ -132,11 +132,11 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
132
132
|
{
|
|
133
133
|
right: position === 'right' ? 12 : undefined,
|
|
134
134
|
alignSelf: position === 'center' ? 'center' : 'flex-end',
|
|
135
|
+
width: widthAnimated,
|
|
135
136
|
height: size === 'small' ? 36 : 48,
|
|
136
137
|
backgroundColor: theme.colors.primary,
|
|
137
138
|
bottom,
|
|
138
139
|
},
|
|
139
|
-
containerStyle,
|
|
140
140
|
]}
|
|
141
141
|
>
|
|
142
142
|
<TouchableOpacity
|
|
@@ -156,9 +156,9 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
156
156
|
{
|
|
157
157
|
fontSize: scaledFontSize,
|
|
158
158
|
lineHeight: scaledLineHeight,
|
|
159
|
+
opacity: opacityAnimated,
|
|
159
160
|
color: 'white',
|
|
160
161
|
},
|
|
161
|
-
labelStyle,
|
|
162
162
|
]}
|
|
163
163
|
numberOfLines={1}
|
|
164
164
|
>
|
package/Layout/Screen.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import React, {
|
|
|
12
12
|
useRef,
|
|
13
13
|
} from 'react';
|
|
14
14
|
import {
|
|
15
|
+
Animated,
|
|
15
16
|
KeyboardAvoidingView,
|
|
16
17
|
NativeScrollEvent,
|
|
17
18
|
NativeSyntheticEvent,
|
|
@@ -24,13 +25,6 @@ import {
|
|
|
24
25
|
View,
|
|
25
26
|
ViewProps,
|
|
26
27
|
} from 'react-native';
|
|
27
|
-
import Animated, {
|
|
28
|
-
runOnJS,
|
|
29
|
-
useAnimatedScrollHandler,
|
|
30
|
-
useSharedValue,
|
|
31
|
-
withTiming,
|
|
32
|
-
type SharedValue,
|
|
33
|
-
} from 'react-native-reanimated';
|
|
34
28
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
35
29
|
import { ApplicationContext, ScreenContext } from '../Context';
|
|
36
30
|
import Navigation from '../Application/Navigation';
|
|
@@ -144,7 +138,7 @@ export interface ScreenProps extends ViewProps {
|
|
|
144
138
|
/**
|
|
145
139
|
* Optional. Animated value for header.
|
|
146
140
|
*/
|
|
147
|
-
animatedValue?:
|
|
141
|
+
animatedValue?: Animated.Value;
|
|
148
142
|
|
|
149
143
|
/**
|
|
150
144
|
* Optional. If `true`, use shadow header.
|
|
@@ -201,12 +195,13 @@ const Screen = forwardRef(
|
|
|
201
195
|
const screen: any = useContext(ScreenContext);
|
|
202
196
|
const insets = useSafeAreaInsets();
|
|
203
197
|
const heightHeader = useHeaderHeight();
|
|
204
|
-
const
|
|
205
|
-
|
|
198
|
+
const animatedValue = useRef<Animated.Value>(
|
|
199
|
+
customAnimatedValue || new Animated.Value(0),
|
|
200
|
+
);
|
|
206
201
|
const currentTint = useRef<string | undefined>(undefined);
|
|
207
202
|
const isTab = navigation?.instance?.getState?.()?.type === 'tab';
|
|
208
203
|
|
|
209
|
-
let handleScroll
|
|
204
|
+
let handleScroll;
|
|
210
205
|
let Component: any = View;
|
|
211
206
|
|
|
212
207
|
let keyboardOffset = heightHeader - Math.min(insets.bottom, 21);
|
|
@@ -253,8 +248,9 @@ const Screen = forwardRef(
|
|
|
253
248
|
interpolate={{
|
|
254
249
|
inputRange: [0, 50],
|
|
255
250
|
outputRange: [1, 0],
|
|
251
|
+
extrapolate: 'clamp',
|
|
256
252
|
}}
|
|
257
|
-
animatedValue={animatedValue}
|
|
253
|
+
animatedValue={animatedValue.current}
|
|
258
254
|
/>
|
|
259
255
|
),
|
|
260
256
|
};
|
|
@@ -269,7 +265,7 @@ const Screen = forwardRef(
|
|
|
269
265
|
headerBackground: (props: any) => (
|
|
270
266
|
<HeaderBackground
|
|
271
267
|
{...props}
|
|
272
|
-
animatedValue={animatedValue}
|
|
268
|
+
animatedValue={animatedValue.current}
|
|
273
269
|
useShadowHeader={useShadowHeader}
|
|
274
270
|
headerBackground={headerBackground}
|
|
275
271
|
gradientColor={gradientColor}
|
|
@@ -288,8 +284,9 @@ const Screen = forwardRef(
|
|
|
288
284
|
interpolate={{
|
|
289
285
|
inputRange: [0, 50],
|
|
290
286
|
outputRange: [1, 0],
|
|
287
|
+
extrapolate: 'clamp',
|
|
291
288
|
}}
|
|
292
|
-
animatedValue={animatedValue}
|
|
289
|
+
animatedValue={animatedValue.current}
|
|
293
290
|
/>
|
|
294
291
|
),
|
|
295
292
|
};
|
|
@@ -324,7 +321,7 @@ const Screen = forwardRef(
|
|
|
324
321
|
headerBackground: (props: any) => (
|
|
325
322
|
<HeaderBackground
|
|
326
323
|
{...props}
|
|
327
|
-
animatedValue={animatedValue}
|
|
324
|
+
animatedValue={animatedValue.current}
|
|
328
325
|
useGradient={false}
|
|
329
326
|
useShadowHeader={useShadowHeader}
|
|
330
327
|
headerBackground={headerBackground}
|
|
@@ -362,8 +359,9 @@ const Screen = forwardRef(
|
|
|
362
359
|
interpolate={{
|
|
363
360
|
inputRange: [0, 50],
|
|
364
361
|
outputRange: [1, 0],
|
|
362
|
+
extrapolate: 'clamp',
|
|
365
363
|
}}
|
|
366
|
-
animatedValue={animatedValue}
|
|
364
|
+
animatedValue={animatedValue.current}
|
|
367
365
|
/>
|
|
368
366
|
),
|
|
369
367
|
};
|
|
@@ -392,7 +390,7 @@ const Screen = forwardRef(
|
|
|
392
390
|
headerLeft: (props: any) =>
|
|
393
391
|
params?.hiddenBack ? null : <HeaderLeft {...props} />,
|
|
394
392
|
headerTitle: () => (
|
|
395
|
-
<SearchHeader {...params} animatedValue={animatedValue} />
|
|
393
|
+
<SearchHeader {...params} animatedValue={animatedValue.current} />
|
|
396
394
|
),
|
|
397
395
|
};
|
|
398
396
|
|
|
@@ -443,51 +441,45 @@ const Screen = forwardRef(
|
|
|
443
441
|
});
|
|
444
442
|
});
|
|
445
443
|
|
|
446
|
-
const onTintColorChange = useCallback(
|
|
447
|
-
(offsetY: number) => {
|
|
448
|
-
if (!animatedHeader) return;
|
|
449
|
-
let color = animatedHeader?.headerTintColor ?? Colors.black_17;
|
|
450
|
-
if (offsetY > 50) {
|
|
451
|
-
color = Colors.black_17;
|
|
452
|
-
}
|
|
453
|
-
if (color !== currentTint.current) {
|
|
454
|
-
currentTint.current = color;
|
|
455
|
-
navigation?.setOptions({
|
|
456
|
-
headerTintColor: color,
|
|
457
|
-
});
|
|
458
|
-
let barStyle: StatusBarStyle = 'dark-content';
|
|
459
|
-
if (currentTint.current === Colors.black_01) {
|
|
460
|
-
barStyle = 'light-content';
|
|
461
|
-
}
|
|
462
|
-
StatusBar.setBarStyle(barStyle, true);
|
|
463
|
-
}
|
|
464
|
-
},
|
|
465
|
-
[animatedHeader, navigation],
|
|
466
|
-
);
|
|
467
|
-
|
|
468
|
-
const emitOnScroll = useCallback(
|
|
469
|
-
(offsetY: number) => {
|
|
470
|
-
scrollViewProps?.onScroll?.({
|
|
471
|
-
nativeEvent: { contentOffset: { x: 0, y: offsetY } },
|
|
472
|
-
} as NativeSyntheticEvent<NativeScrollEvent>);
|
|
473
|
-
},
|
|
474
|
-
[scrollViewProps],
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
const scrollHandler = useAnimatedScrollHandler({
|
|
478
|
-
onScroll: (event) => {
|
|
479
|
-
animatedValue.value = event.contentOffset.y;
|
|
480
|
-
runOnJS(emitOnScroll)(event.contentOffset.y);
|
|
481
|
-
runOnJS(onTintColorChange)(event.contentOffset.y);
|
|
482
|
-
},
|
|
483
|
-
});
|
|
484
|
-
|
|
485
444
|
/**
|
|
486
445
|
* animated when use scroll && animated value
|
|
487
446
|
*/
|
|
488
447
|
if (scrollable) {
|
|
489
448
|
Component = Animated.ScrollView;
|
|
490
|
-
handleScroll =
|
|
449
|
+
handleScroll = Animated.event(
|
|
450
|
+
[
|
|
451
|
+
{
|
|
452
|
+
nativeEvent: {
|
|
453
|
+
contentOffset: { y: animatedValue.current as Animated.Value },
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
{
|
|
458
|
+
useNativeDriver: true,
|
|
459
|
+
listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
460
|
+
scrollViewProps?.onScroll?.(e);
|
|
461
|
+
if (animatedHeader) {
|
|
462
|
+
const offsetY = e.nativeEvent.contentOffset.y;
|
|
463
|
+
let color = animatedHeader?.headerTintColor ?? Colors.black_17;
|
|
464
|
+
if (offsetY > 50) {
|
|
465
|
+
color = Colors.black_17;
|
|
466
|
+
}
|
|
467
|
+
if (color !== currentTint.current) {
|
|
468
|
+
currentTint.current = color;
|
|
469
|
+
navigation?.setOptions({
|
|
470
|
+
headerTintColor: color,
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
let barStyle: StatusBarStyle = 'dark-content';
|
|
474
|
+
if (currentTint.current === Colors.black_01) {
|
|
475
|
+
barStyle = 'light-content';
|
|
476
|
+
}
|
|
477
|
+
StatusBar.setBarStyle(barStyle, true);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
);
|
|
491
483
|
}
|
|
492
484
|
|
|
493
485
|
/**
|
|
@@ -497,7 +489,11 @@ const Screen = forwardRef(
|
|
|
497
489
|
const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
498
490
|
const offsetY = e.nativeEvent.contentOffset.y;
|
|
499
491
|
if (inputSearchProps && offsetY < 100 && offsetY > 0) {
|
|
500
|
-
animatedValue.
|
|
492
|
+
Animated.timing(animatedValue.current, {
|
|
493
|
+
toValue: 0,
|
|
494
|
+
useNativeDriver: true,
|
|
495
|
+
duration: 300,
|
|
496
|
+
}).start();
|
|
501
497
|
ref?.scrollTo?.({ y: 0, animated: true });
|
|
502
498
|
}
|
|
503
499
|
scrollViewProps?.onScrollEndDrag?.(e);
|
|
@@ -513,7 +509,7 @@ const Screen = forwardRef(
|
|
|
513
509
|
style={[styles.screenBanner, { maxHeight: 210 + layoutOffset }]}
|
|
514
510
|
>
|
|
515
511
|
{animatedHeader?.component({
|
|
516
|
-
animatedValue: animatedValue,
|
|
512
|
+
animatedValue: animatedValue.current,
|
|
517
513
|
})}
|
|
518
514
|
</View>
|
|
519
515
|
);
|
|
@@ -580,7 +576,7 @@ const Screen = forwardRef(
|
|
|
580
576
|
headerType={headerType}
|
|
581
577
|
heightHeader={heightHeader}
|
|
582
578
|
headerRightWidth={headerRightWidth}
|
|
583
|
-
animatedValue={animatedValue}
|
|
579
|
+
animatedValue={animatedValue.current}
|
|
584
580
|
inputSearchProps={inputSearchProps}
|
|
585
581
|
navigation={navigation}
|
|
586
582
|
inputSearchRef={inputSearchRef}
|
|
@@ -618,7 +614,7 @@ const Screen = forwardRef(
|
|
|
618
614
|
<View>
|
|
619
615
|
<FloatingButton
|
|
620
616
|
{...floatingButtonProps}
|
|
621
|
-
animatedValue={animatedValue}
|
|
617
|
+
animatedValue={animatedValue.current}
|
|
622
618
|
bottom={
|
|
623
619
|
Footer || isTab ? 12 : Math.min(insets.bottom, 21) + Spacing.S
|
|
624
620
|
}
|