@momo-kits/foundation 0.161.1-beta.2 → 0.161.2-beta.5
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/BottomSheet.tsx +2 -2
- package/Application/BottomTab/BottomTabBar.tsx +15 -15
- package/Application/BottomTab/index.tsx +31 -55
- package/Application/Components/BackgroundImageView.tsx +22 -6
- package/Application/Components/HeaderAnimated.tsx +32 -29
- package/Application/Components/HeaderBackground.tsx +20 -26
- package/Application/Components/HeaderExtendHeader.tsx +114 -94
- package/Application/Components/HeaderTitle.tsx +39 -10
- package/Application/Components/SearchHeader.tsx +16 -21
- package/Application/types.ts +4 -4
- package/Application/utils.tsx +4 -3
- package/Badge/BadgeDotAnimation.tsx +53 -71
- package/Button/index.tsx +1 -4
- package/Image/index.tsx +12 -2
- package/Input/InputOTP.tsx +38 -23
- package/Input/InputTextArea.tsx +9 -3
- package/Layout/FloatingButton.tsx +59 -59
- package/Layout/Screen.tsx +61 -57
- package/Loader/ProgressBar.tsx +20 -18
- package/Pagination/Dot.tsx +2 -2
- package/Pagination/PaginationScroll.tsx +31 -27
- package/Skeleton/index.tsx +32 -24
- package/Text/utils.ts +1 -1
- package/package.json +34 -34
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import React, { Ref, useContext
|
|
1
|
+
import React, { Ref, useContext } 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';
|
|
3
11
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
4
|
-
import { Animated, Dimensions, Platform, StyleSheet, View } from 'react-native';
|
|
5
12
|
import { HeaderType } from '../../Layout/types';
|
|
6
13
|
import { InputRef, InputSearch } from '../../Input';
|
|
7
14
|
import Navigation from '../Navigation';
|
|
@@ -14,15 +21,10 @@ const SCREEN_PADDING = 12;
|
|
|
14
21
|
const BACK_WIDTH = 28;
|
|
15
22
|
|
|
16
23
|
const { width: screenWidth } = Dimensions.get('window');
|
|
17
|
-
const LinearGradientAnimated = Animated.createAnimatedComponent(LinearGradient);
|
|
18
24
|
|
|
19
|
-
/**
|
|
20
|
-
* Header extended with background image
|
|
21
|
-
* @constructor
|
|
22
|
-
*/
|
|
23
25
|
const HeaderExtendHeader: React.FC<{
|
|
24
26
|
headerType?: HeaderType;
|
|
25
|
-
animatedValue:
|
|
27
|
+
animatedValue: SharedValue<number>;
|
|
26
28
|
heightHeader: number;
|
|
27
29
|
headerRightWidth: number;
|
|
28
30
|
inputSearchProps?: SearchHeaderProps;
|
|
@@ -38,118 +40,132 @@ const HeaderExtendHeader: React.FC<{
|
|
|
38
40
|
headerRightWidth = 73,
|
|
39
41
|
inputSearchProps,
|
|
40
42
|
inputSearchRef,
|
|
41
|
-
useShadowHeader = true,
|
|
43
|
+
useShadowHeader: useShadowHeaderProp = true,
|
|
42
44
|
gradientColor: customGradientColor,
|
|
43
45
|
headerBackground: customBackground,
|
|
44
46
|
}) => {
|
|
45
47
|
const { theme } = useContext(ApplicationContext);
|
|
46
48
|
const context = useContext<any>(MiniAppContext);
|
|
47
|
-
const
|
|
49
|
+
const fallback = useSharedValue(0);
|
|
50
|
+
const sv = animatedValue ?? fallback;
|
|
48
51
|
const gradientColor = customGradientColor ?? theme.colors.gradient;
|
|
49
52
|
const headerBackground = customBackground ?? theme.assets?.headerBackground;
|
|
50
53
|
const leftPosition = inputSearchProps?.leftPosition || BACK_WIDTH + 20;
|
|
51
54
|
|
|
52
|
-
|
|
55
|
+
let useShadowHeader = useShadowHeaderProp;
|
|
56
|
+
if (inputSearchProps && Platform.OS === 'android') {
|
|
57
|
+
useShadowHeader = false;
|
|
58
|
+
}
|
|
53
59
|
|
|
54
|
-
const
|
|
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
|
-
});
|
|
60
|
+
const showBaseLineDebug = context?.designSystemConfig?.isBaselineEnabled;
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
62
|
+
const heightStyle = useAnimatedStyle(() => ({
|
|
63
|
+
height: interpolate(
|
|
64
|
+
sv.value,
|
|
65
|
+
[0, 100],
|
|
66
|
+
[heightHeader + 52, heightHeader],
|
|
67
|
+
Extrapolation.CLAMP,
|
|
68
|
+
),
|
|
69
|
+
}));
|
|
73
70
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
extrapolate: 'clamp',
|
|
78
|
-
});
|
|
71
|
+
const gradientOpacityStyle = useAnimatedStyle(() => ({
|
|
72
|
+
opacity: interpolate(sv.value, [0, 52], [1, 0], Extrapolation.CLAMP),
|
|
73
|
+
}));
|
|
79
74
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
theme.colors.background.default,
|
|
75
|
+
const searchTranslateStyle = useAnimatedStyle(() => ({
|
|
76
|
+
transform: [
|
|
77
|
+
{
|
|
78
|
+
translateX: interpolate(
|
|
79
|
+
sv.value,
|
|
80
|
+
[0, 100],
|
|
81
|
+
[SCREEN_PADDING, leftPosition],
|
|
82
|
+
Extrapolation.CLAMP,
|
|
83
|
+
),
|
|
84
|
+
},
|
|
91
85
|
],
|
|
92
|
-
|
|
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
|
+
}));
|
|
96
|
+
|
|
97
|
+
const searchBackgroundStyle = useAnimatedStyle(() => {
|
|
98
|
+
const t = Math.min(Math.max(sv.value / 100, 0), 1);
|
|
99
|
+
return { backgroundColor: t === 0
|
|
100
|
+
? theme.colors.background.surface
|
|
101
|
+
: theme.colors.background.default };
|
|
93
102
|
});
|
|
94
103
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
const extendedHeightStyle = useAnimatedStyle(() => ({
|
|
105
|
+
height: interpolate(
|
|
106
|
+
sv.value,
|
|
107
|
+
[0, 100],
|
|
108
|
+
[heightHeader + 52, heightHeader],
|
|
109
|
+
Extrapolation.CLAMP,
|
|
110
|
+
),
|
|
111
|
+
}));
|
|
98
112
|
|
|
99
113
|
if (inputSearchProps) {
|
|
100
114
|
return (
|
|
101
115
|
<View style={[{ zIndex: 0 }, showBaseLineDebug && styles.debugBaseLine]}>
|
|
102
|
-
<Animated.View style={
|
|
116
|
+
<Animated.View style={heightStyle} />
|
|
103
117
|
<BackgroundImageView
|
|
104
118
|
useShadowHeader={useShadowHeader}
|
|
105
119
|
heightHeader={heightHeader}
|
|
106
|
-
|
|
120
|
+
animatedValue={sv}
|
|
107
121
|
headerBackground={headerBackground}
|
|
108
122
|
/>
|
|
109
123
|
<Animated.View
|
|
110
124
|
style={[
|
|
111
125
|
styles.headerBox,
|
|
112
|
-
|
|
126
|
+
headerType === 'extended'
|
|
127
|
+
? heightStyle
|
|
128
|
+
: { height: heightHeader },
|
|
113
129
|
]}
|
|
114
130
|
>
|
|
115
131
|
{!!gradientColor && (
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
style={[styles.extendedHeader, { opacity: opacityGradient }]}
|
|
132
|
+
<Animated.View
|
|
133
|
+
style={[styles.extendedHeader, gradientOpacityStyle]}
|
|
119
134
|
>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
135
|
+
<LinearGradient
|
|
136
|
+
colors={[gradientColor, gradientColor + '00']}
|
|
137
|
+
style={StyleSheet.absoluteFill}
|
|
138
|
+
>
|
|
139
|
+
{!!theme.assets?.headerBackground && (
|
|
140
|
+
<Image
|
|
141
|
+
style={styles.headerBackground}
|
|
142
|
+
source={{ uri: theme.assets?.headerBackground }}
|
|
143
|
+
loading={false}
|
|
144
|
+
/>
|
|
145
|
+
)}
|
|
146
|
+
</LinearGradient>
|
|
147
|
+
</Animated.View>
|
|
128
148
|
)}
|
|
129
149
|
</Animated.View>
|
|
130
150
|
<Animated.View
|
|
131
|
-
style={
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
151
|
+
style={[
|
|
152
|
+
{
|
|
153
|
+
justifyContent: 'flex-end',
|
|
154
|
+
position: 'absolute',
|
|
155
|
+
zIndex: 2,
|
|
156
|
+
},
|
|
157
|
+
heightStyle,
|
|
158
|
+
]}
|
|
137
159
|
>
|
|
138
160
|
<Animated.View
|
|
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
|
-
}}
|
|
161
|
+
style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
|
|
151
162
|
>
|
|
152
|
-
<Animated.View
|
|
163
|
+
<Animated.View
|
|
164
|
+
style={[
|
|
165
|
+
{ borderRadius: Radius.XL },
|
|
166
|
+
searchBackgroundStyle,
|
|
167
|
+
]}
|
|
168
|
+
>
|
|
153
169
|
<InputSearch
|
|
154
170
|
{...inputSearchProps}
|
|
155
171
|
ref={inputSearchRef}
|
|
@@ -169,22 +185,26 @@ const HeaderExtendHeader: React.FC<{
|
|
|
169
185
|
<BackgroundImageView
|
|
170
186
|
useShadowHeader={useShadowHeader}
|
|
171
187
|
heightHeader={heightHeader}
|
|
172
|
-
|
|
188
|
+
animatedValue={sv}
|
|
173
189
|
headerBackground={headerBackground}
|
|
174
190
|
/>
|
|
175
191
|
{!!gradientColor && (
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
style={[styles.extendedHeader, { opacity: opacityGradient }]}
|
|
192
|
+
<Animated.View
|
|
193
|
+
style={[styles.extendedHeader, gradientOpacityStyle, extendedHeightStyle]}
|
|
179
194
|
>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
195
|
+
<LinearGradient
|
|
196
|
+
colors={[gradientColor, gradientColor + '00']}
|
|
197
|
+
style={StyleSheet.absoluteFill}
|
|
198
|
+
>
|
|
199
|
+
{!!headerBackground && (
|
|
200
|
+
<Image
|
|
201
|
+
style={styles.headerBackground}
|
|
202
|
+
source={{ uri: headerBackground }}
|
|
203
|
+
loading={false}
|
|
204
|
+
/>
|
|
205
|
+
)}
|
|
206
|
+
</LinearGradient>
|
|
207
|
+
</Animated.View>
|
|
188
208
|
)}
|
|
189
209
|
<View style={{ height: heightHeader }} />
|
|
190
210
|
</View>
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import React, { useContext } from 'react';
|
|
2
2
|
import {
|
|
3
|
-
Animated,
|
|
4
3
|
Dimensions,
|
|
5
4
|
StyleSheet,
|
|
6
5
|
TouchableOpacity,
|
|
7
6
|
View,
|
|
8
7
|
} from 'react-native';
|
|
8
|
+
import Animated, {
|
|
9
|
+
Extrapolation,
|
|
10
|
+
interpolate,
|
|
11
|
+
useAnimatedStyle,
|
|
12
|
+
type SharedValue,
|
|
13
|
+
} from 'react-native-reanimated';
|
|
9
14
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
10
15
|
import { exportFontFamily, Text, useScaleSize } from '../../Text';
|
|
11
16
|
import { Colors, Radius, Spacing, Styles } from '../../Consts';
|
|
@@ -18,21 +23,45 @@ import { Image } from '../../Image';
|
|
|
18
23
|
import { Icon } from '../../Icon';
|
|
19
24
|
import { Skeleton } from '../../Skeleton';
|
|
20
25
|
|
|
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
|
+
|
|
21
38
|
/**
|
|
22
39
|
* default header title used for nav
|
|
23
40
|
*/
|
|
24
|
-
const HeaderTitle: React.FC<any> = props => {
|
|
41
|
+
const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = props => {
|
|
25
42
|
const context = useContext<any>(MiniAppContext);
|
|
26
43
|
|
|
27
44
|
const showBaseLineDebug = context?.designSystemConfig?.isBaselineEnabled;
|
|
28
45
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
)
|
|
46
|
+
const interpolateConfig = props.interpolate ?? {
|
|
47
|
+
inputRange: [0, 200],
|
|
48
|
+
outputRange: [0, 1],
|
|
49
|
+
};
|
|
50
|
+
const animatedValue = props.animatedValue;
|
|
51
|
+
|
|
52
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
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
|
+
});
|
|
36
65
|
|
|
37
66
|
return (
|
|
38
67
|
<View
|
|
@@ -57,9 +86,9 @@ const HeaderTitle: React.FC<any> = props => {
|
|
|
57
86
|
},
|
|
58
87
|
{
|
|
59
88
|
fontFamily: exportFontFamily('bold', 'action_xs_bold'),
|
|
60
|
-
opacity,
|
|
61
89
|
color: props.tintColor,
|
|
62
90
|
},
|
|
91
|
+
animatedStyle,
|
|
63
92
|
]}
|
|
64
93
|
numberOfLines={1}
|
|
65
94
|
/>
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { Colors, Radius, Spacing, Styles } from '../../Consts';
|
|
2
2
|
import { InputRef, InputSearch } from '../../Input';
|
|
3
3
|
import {
|
|
4
|
-
Animated,
|
|
5
4
|
Dimensions,
|
|
6
5
|
StyleSheet,
|
|
7
6
|
TouchableOpacity,
|
|
8
7
|
View,
|
|
9
8
|
} from 'react-native';
|
|
10
|
-
import
|
|
9
|
+
import Animated, {
|
|
10
|
+
useAnimatedStyle,
|
|
11
|
+
useSharedValue,
|
|
12
|
+
} from 'react-native-reanimated';
|
|
13
|
+
import React, { useContext } from 'react';
|
|
11
14
|
import { SearchHeaderProps } from '../types';
|
|
12
15
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
13
16
|
import { Text } from '../../Text';
|
|
@@ -28,28 +31,18 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
28
31
|
const BACK_WIDTH = 28;
|
|
29
32
|
const { width: screenWidth } = Dimensions.get('window');
|
|
30
33
|
|
|
31
|
-
const
|
|
34
|
+
const fallback = useSharedValue(0);
|
|
35
|
+
const sv = animatedValue ?? fallback;
|
|
32
36
|
const leftPosition = props?.leftPosition ?? BACK_WIDTH + 20;
|
|
33
37
|
const headerRightWidth = props?.headerRightWidth ?? 73;
|
|
34
38
|
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
animatedValue?.removeListener(listener);
|
|
42
|
-
}
|
|
39
|
+
const backgroundStyle = useAnimatedStyle(() => {
|
|
40
|
+
const t = Math.min(Math.max(sv.value / 100, 0), 1);
|
|
41
|
+
return {
|
|
42
|
+
backgroundColor: t === 0
|
|
43
|
+
? theme.colors.background.surface
|
|
44
|
+
: theme.colors.background.default,
|
|
43
45
|
};
|
|
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',
|
|
53
46
|
});
|
|
54
47
|
|
|
55
48
|
const goBack = () => {
|
|
@@ -91,7 +84,9 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
91
84
|
},
|
|
92
85
|
]}
|
|
93
86
|
>
|
|
94
|
-
<Animated.View
|
|
87
|
+
<Animated.View
|
|
88
|
+
style={[{ borderRadius: Radius.XL }, backgroundStyle]}
|
|
89
|
+
>
|
|
95
90
|
<InputSearch
|
|
96
91
|
ref={ref}
|
|
97
92
|
{...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,
|
|
6
5
|
TouchableOpacityProps,
|
|
7
6
|
ViewProps,
|
|
8
7
|
ViewStyle,
|
|
9
8
|
} 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';
|
|
@@ -252,7 +252,7 @@ export interface HeaderBackProps extends NavigationButtonProps {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
export type HeaderBackgroundProps = {
|
|
255
|
-
animatedValue?:
|
|
255
|
+
animatedValue?: SharedValue<number>;
|
|
256
256
|
useGradient?: boolean;
|
|
257
257
|
useShadowHeader?: boolean;
|
|
258
258
|
backgroundColor?: string;
|
|
@@ -292,7 +292,7 @@ export type TitleJourneyProps = {
|
|
|
292
292
|
};
|
|
293
293
|
|
|
294
294
|
export interface HeaderAnimatedProps extends ViewProps {
|
|
295
|
-
animatedValue:
|
|
295
|
+
animatedValue: SharedValue<number>;
|
|
296
296
|
image: string;
|
|
297
297
|
useScale?: boolean;
|
|
298
298
|
}
|
|
@@ -336,7 +336,7 @@ export type AnimatedHeader = {
|
|
|
336
336
|
|
|
337
337
|
export interface SearchHeaderProps extends InputSearchProps {
|
|
338
338
|
ref?: React.RefObject<InputRef>;
|
|
339
|
-
animatedValue?:
|
|
339
|
+
animatedValue?: SharedValue<number>;
|
|
340
340
|
headerRightWidth?: 0 | 74 | 110 | number;
|
|
341
341
|
leftPosition?: 12 | 48 | number;
|
|
342
342
|
renderButtons?: () => ReactNode;
|
package/Application/utils.tsx
CHANGED
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
} from '@react-navigation/stack';
|
|
6
6
|
import type { HeaderTitleProps, NavigationOptions } from './types';
|
|
7
7
|
import { Colors, Spacing } from '../Consts';
|
|
8
|
-
import {
|
|
8
|
+
import { AppState, Platform } from 'react-native';
|
|
9
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
9
10
|
import {
|
|
10
11
|
MiniAppContext,
|
|
11
12
|
ScreenContext,
|
|
@@ -60,7 +61,7 @@ const getModalOptions = (): StackNavigationOptions => {
|
|
|
60
61
|
*/
|
|
61
62
|
const getOptions = (
|
|
62
63
|
params: NavigationOptions,
|
|
63
|
-
animatedValue?:
|
|
64
|
+
animatedValue?: SharedValue<number>,
|
|
64
65
|
) => {
|
|
65
66
|
let options: StackNavigationOptions = {};
|
|
66
67
|
|
|
@@ -131,7 +132,7 @@ const getOptions = (
|
|
|
131
132
|
|
|
132
133
|
const exportHeaderTitle = (
|
|
133
134
|
params: NavigationOptions,
|
|
134
|
-
animatedValue?:
|
|
135
|
+
animatedValue?: SharedValue<number>,
|
|
135
136
|
): StackNavigationOptions => {
|
|
136
137
|
if (typeof params.headerTitle === 'object') {
|
|
137
138
|
return {
|
|
@@ -1,93 +1,75 @@
|
|
|
1
|
-
import React, { useEffect
|
|
2
|
-
import {
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
cancelAnimation,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
useSharedValue,
|
|
7
|
+
withRepeat,
|
|
8
|
+
withSequence,
|
|
9
|
+
withSpring,
|
|
10
|
+
withTiming,
|
|
11
|
+
} from 'react-native-reanimated';
|
|
3
12
|
import { BadgeDotProps } from './types';
|
|
4
13
|
import styles from './styles';
|
|
5
14
|
|
|
6
15
|
const DURATION = 500;
|
|
7
16
|
|
|
8
17
|
const BadgeDotAnimation = ({ size, style }: BadgeDotProps) => {
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const waveOpacityAnim = useRef(new Animated.Value(0)).current;
|
|
18
|
+
const scaleAnim = useSharedValue(1);
|
|
19
|
+
const waveScaleAnim = useSharedValue(1);
|
|
20
|
+
const waveOpacityAnim = useSharedValue(0);
|
|
13
21
|
|
|
14
22
|
const dotStyle =
|
|
15
23
|
size === 'small' ? styles.dotAnimationSmall : styles.dotAnimation;
|
|
16
24
|
const waveStyle = size === 'small' ? styles.waveSmall : styles.wave;
|
|
17
25
|
|
|
18
26
|
useEffect(() => {
|
|
19
|
-
|
|
20
|
-
const animation = Animated.loop(
|
|
21
|
-
Animated.parallel([
|
|
22
|
-
// Dot pulse animation
|
|
23
|
-
Animated.sequence([
|
|
24
|
-
Animated.spring(scaleAnim, {
|
|
25
|
-
toValue: 1, // Scale up slightly
|
|
26
|
-
friction: 5, // Controls the "bounciness" of the spring
|
|
27
|
-
tension: 30, // Controls the "stiffness" of the spring
|
|
28
|
-
useNativeDriver: true,
|
|
29
|
-
}),
|
|
30
|
-
Animated.spring(scaleAnim, {
|
|
31
|
-
toValue: 1.1,
|
|
32
|
-
friction: 5,
|
|
33
|
-
tension: 30,
|
|
34
|
-
useNativeDriver: true,
|
|
35
|
-
}),
|
|
36
|
-
]), // Wave animation
|
|
37
|
-
Animated.sequence([
|
|
38
|
-
Animated.timing(waveScaleAnim, {
|
|
39
|
-
toValue: 2.5,
|
|
40
|
-
duration: DURATION * 3,
|
|
41
|
-
useNativeDriver: true,
|
|
42
|
-
}),
|
|
43
|
-
Animated.timing(waveScaleAnim, {
|
|
44
|
-
toValue: 1, // Reset wave size
|
|
45
|
-
duration: 0,
|
|
46
|
-
useNativeDriver: true,
|
|
47
|
-
}),
|
|
48
|
-
]), // Wave opacity animation
|
|
49
|
-
Animated.sequence([
|
|
50
|
-
Animated.timing(waveOpacityAnim, {
|
|
51
|
-
toValue: 0.3, // Wave becomes visible
|
|
52
|
-
duration: DURATION * 2,
|
|
53
|
-
useNativeDriver: true,
|
|
54
|
-
}),
|
|
55
|
-
Animated.timing(waveOpacityAnim, {
|
|
56
|
-
toValue: 0, // Wave fades out
|
|
57
|
-
duration: DURATION,
|
|
58
|
-
useNativeDriver: true,
|
|
59
|
-
}),
|
|
60
|
-
]),
|
|
61
|
-
]),
|
|
62
|
-
);
|
|
63
|
-
animation.start();
|
|
27
|
+
const springCfg = { damping: 5, stiffness: 30, mass: 1 };
|
|
64
28
|
|
|
29
|
+
scaleAnim.value = withRepeat(
|
|
30
|
+
withSequence(
|
|
31
|
+
withSpring(1, springCfg),
|
|
32
|
+
withSpring(1.1, springCfg),
|
|
33
|
+
),
|
|
34
|
+
-1,
|
|
35
|
+
false,
|
|
36
|
+
);
|
|
37
|
+
waveScaleAnim.value = withRepeat(
|
|
38
|
+
withSequence(
|
|
39
|
+
withTiming(2.5, { duration: DURATION * 3 }),
|
|
40
|
+
withTiming(1, { duration: 0 }),
|
|
41
|
+
),
|
|
42
|
+
-1,
|
|
43
|
+
false,
|
|
44
|
+
);
|
|
45
|
+
waveOpacityAnim.value = withRepeat(
|
|
46
|
+
withSequence(
|
|
47
|
+
withTiming(0.3, { duration: DURATION * 2 }),
|
|
48
|
+
withTiming(0, { duration: DURATION }),
|
|
49
|
+
),
|
|
50
|
+
-1,
|
|
51
|
+
false,
|
|
52
|
+
);
|
|
65
53
|
return () => {
|
|
66
|
-
|
|
54
|
+
cancelAnimation(scaleAnim);
|
|
55
|
+
cancelAnimation(waveScaleAnim);
|
|
56
|
+
cancelAnimation(waveOpacityAnim);
|
|
67
57
|
};
|
|
68
58
|
}, [scaleAnim, waveOpacityAnim, waveScaleAnim]);
|
|
69
59
|
|
|
60
|
+
const waveAnimatedStyle = useAnimatedStyle(() => ({
|
|
61
|
+
transform: [{ scale: waveScaleAnim.value }],
|
|
62
|
+
opacity: waveOpacityAnim.value,
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
const dotAnimatedStyle = useAnimatedStyle(() => ({
|
|
66
|
+
transform: [{ scale: scaleAnim.value }],
|
|
67
|
+
}));
|
|
68
|
+
|
|
70
69
|
return (
|
|
71
70
|
<View style={[styles.dotAnimationContainer, style]}>
|
|
72
|
-
{
|
|
73
|
-
<Animated.View
|
|
74
|
-
style={[
|
|
75
|
-
waveStyle,
|
|
76
|
-
{
|
|
77
|
-
transform: [{ scale: waveScaleAnim }],
|
|
78
|
-
opacity: waveOpacityAnim,
|
|
79
|
-
},
|
|
80
|
-
]}
|
|
81
|
-
/>
|
|
82
|
-
{/* Dot Animation */}
|
|
83
|
-
<Animated.View
|
|
84
|
-
style={[
|
|
85
|
-
dotStyle,
|
|
86
|
-
{
|
|
87
|
-
transform: [{ scale: scaleAnim }],
|
|
88
|
-
},
|
|
89
|
-
]}
|
|
90
|
-
/>
|
|
71
|
+
<Animated.View style={[waveStyle, waveAnimatedStyle]} />
|
|
72
|
+
<Animated.View style={[dotStyle, dotAnimatedStyle]} />
|
|
91
73
|
</View>
|
|
92
74
|
);
|
|
93
75
|
};
|
package/Button/index.tsx
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { FC, useContext, useRef } from 'react';
|
|
2
2
|
import {
|
|
3
|
-
Animated,
|
|
4
3
|
StyleSheet,
|
|
5
4
|
TouchableOpacity,
|
|
6
5
|
TouchableOpacityProps,
|
|
@@ -29,8 +28,6 @@ import Reanimated, {
|
|
|
29
28
|
withTiming,
|
|
30
29
|
} from 'react-native-reanimated';
|
|
31
30
|
|
|
32
|
-
const AnimationLinear = Animated.createAnimatedComponent(LinearGradient);
|
|
33
|
-
|
|
34
31
|
export interface ButtonProps extends TouchableOpacityProps {
|
|
35
32
|
/**
|
|
36
33
|
* Defines the visual style of the button.
|
|
@@ -359,7 +356,7 @@ const Button: FC<ButtonProps> = ({
|
|
|
359
356
|
{renderTitle()}
|
|
360
357
|
{renderIcon('right')}
|
|
361
358
|
{gradientPros && (
|
|
362
|
-
<
|
|
359
|
+
<LinearGradient {...gradientPros} style={styles.gradientView} />
|
|
363
360
|
)}
|
|
364
361
|
{gradientPros && <View style={styles.strokeView} />}
|
|
365
362
|
</Reanimated.View>
|