@momo-kits/foundation 0.161.2-beta.8 → 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/BottomSheet.tsx +2 -2
- package/Application/BottomTab/BottomTabBar.tsx +15 -15
- package/Application/BottomTab/index.tsx +31 -55
- package/Badge/BadgeDotAnimation.tsx +53 -71
- package/Button/index.tsx +1 -4
- package/Input/InputOTP.tsx +38 -23
- 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/package.json +1 -1
|
@@ -20,7 +20,7 @@ import { Icon } from '../Icon';
|
|
|
20
20
|
import { useHeaderHeight } from '@react-navigation/elements';
|
|
21
21
|
import Animated, {
|
|
22
22
|
Easing,
|
|
23
|
-
|
|
23
|
+
Extrapolation,
|
|
24
24
|
interpolate,
|
|
25
25
|
runOnJS,
|
|
26
26
|
useAnimatedStyle,
|
|
@@ -242,7 +242,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
242
242
|
translateY.value,
|
|
243
243
|
[0, heightDevice],
|
|
244
244
|
[1, 0],
|
|
245
|
-
|
|
245
|
+
Extrapolation.CLAMP,
|
|
246
246
|
),
|
|
247
247
|
};
|
|
248
248
|
});
|
|
@@ -2,13 +2,17 @@ import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
|
|
|
2
2
|
import { CommonActions, Route } from '@react-navigation/native';
|
|
3
3
|
import React, { useCallback, useContext } from 'react';
|
|
4
4
|
import {
|
|
5
|
-
Animated,
|
|
6
5
|
Platform,
|
|
7
6
|
StyleSheet,
|
|
8
7
|
TouchableOpacity,
|
|
9
8
|
useWindowDimensions,
|
|
10
9
|
View,
|
|
11
10
|
} from 'react-native';
|
|
11
|
+
import Animated, {
|
|
12
|
+
useAnimatedStyle,
|
|
13
|
+
useSharedValue,
|
|
14
|
+
withTiming,
|
|
15
|
+
} from 'react-native-reanimated';
|
|
12
16
|
import { EdgeInsets, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
13
17
|
import { Colors, Radius, Styles } from '../../Consts';
|
|
14
18
|
import { Icon } from '../../Icon';
|
|
@@ -57,8 +61,13 @@ export default function BottomTabBar({
|
|
|
57
61
|
const useFloating = floatingButton && state.routes.length % 2 === 0;
|
|
58
62
|
const itemWidth = widthDevice / (state.routes.length + (useFloating ? 1 : 0));
|
|
59
63
|
// const buildLink = useLinkBuilder();
|
|
60
|
-
const indicatorAnimated =
|
|
64
|
+
const indicatorAnimated = useSharedValue(0);
|
|
61
65
|
const insets = useSafeAreaInsets();
|
|
66
|
+
|
|
67
|
+
const indicatorStyle = useAnimatedStyle(() => ({
|
|
68
|
+
width: itemWidth,
|
|
69
|
+
transform: [{ translateX: indicatorAnimated.value }],
|
|
70
|
+
}));
|
|
62
71
|
const paddingBottom = getPaddingBottom(insets);
|
|
63
72
|
const focusedTab = state?.routes[state.index]?.name || routes[0]?.params;
|
|
64
73
|
|
|
@@ -199,11 +208,7 @@ export default function BottomTabBar({
|
|
|
199
208
|
if (useFloating && index > (state.routes.length - 1) / 2) {
|
|
200
209
|
index += 1;
|
|
201
210
|
}
|
|
202
|
-
|
|
203
|
-
toValue: itemWidth * index,
|
|
204
|
-
useNativeDriver: true,
|
|
205
|
-
duration: 200,
|
|
206
|
-
}).start();
|
|
211
|
+
indicatorAnimated.value = withTiming(itemWidth * index, { duration: 200 });
|
|
207
212
|
}, [
|
|
208
213
|
state.index,
|
|
209
214
|
itemWidth,
|
|
@@ -232,7 +237,7 @@ export default function BottomTabBar({
|
|
|
232
237
|
};
|
|
233
238
|
|
|
234
239
|
return (
|
|
235
|
-
<
|
|
240
|
+
<View
|
|
236
241
|
style={[
|
|
237
242
|
floatingButton ? styles.tabBarFloatingButton : styles.tabBar,
|
|
238
243
|
{
|
|
@@ -250,12 +255,7 @@ export default function BottomTabBar({
|
|
|
250
255
|
<View style={styles.content} accessibilityLabel={'bottom_tab_bar'}>
|
|
251
256
|
{useFloating && floatingButton?.container}
|
|
252
257
|
<Animated.View
|
|
253
|
-
style={{
|
|
254
|
-
width: itemWidth,
|
|
255
|
-
position: 'absolute',
|
|
256
|
-
top: 0,
|
|
257
|
-
transform: [{ translateX: indicatorAnimated }],
|
|
258
|
-
}}
|
|
258
|
+
style={[{ position: 'absolute', top: 0 }, indicatorStyle]}
|
|
259
259
|
>
|
|
260
260
|
<View
|
|
261
261
|
style={[
|
|
@@ -266,7 +266,7 @@ export default function BottomTabBar({
|
|
|
266
266
|
</Animated.View>
|
|
267
267
|
{renderTabBar()}
|
|
268
268
|
</View>
|
|
269
|
-
</
|
|
269
|
+
</View>
|
|
270
270
|
);
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -5,16 +5,20 @@ import {
|
|
|
5
5
|
useFocusEffect,
|
|
6
6
|
} from '@react-navigation/native';
|
|
7
7
|
import { createStackNavigator } from '@react-navigation/stack';
|
|
8
|
-
import React, { useContext, useEffect, useLayoutEffect
|
|
8
|
+
import React, { useContext, useEffect, useLayoutEffect } from 'react';
|
|
9
9
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
10
10
|
import {
|
|
11
|
-
Animated,
|
|
12
11
|
Platform,
|
|
13
12
|
StatusBar,
|
|
14
13
|
StyleSheet,
|
|
15
14
|
useWindowDimensions,
|
|
16
15
|
View,
|
|
17
16
|
} from 'react-native';
|
|
17
|
+
import Animated, {
|
|
18
|
+
useAnimatedStyle,
|
|
19
|
+
useSharedValue,
|
|
20
|
+
withTiming,
|
|
21
|
+
} from 'react-native-reanimated';
|
|
18
22
|
import { Colors } from '../../Consts';
|
|
19
23
|
import { Icon } from '../../Icon';
|
|
20
24
|
import { exportFontFamily, Text } from '../../Text';
|
|
@@ -35,11 +39,16 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
|
|
|
35
39
|
|
|
36
40
|
const opacityValue = 0.3;
|
|
37
41
|
const scaleValue = 0.97;
|
|
38
|
-
const opacity =
|
|
39
|
-
const scale =
|
|
42
|
+
const opacity = useSharedValue(opacityValue);
|
|
43
|
+
const scale = useSharedValue(scaleValue);
|
|
40
44
|
const screenName = screen?.name || screen?.type?.name || 'Invalid';
|
|
41
45
|
const { isBackgroundToForeground } = useAppState();
|
|
42
46
|
|
|
47
|
+
const tabAnimatedStyle = useAnimatedStyle(() => ({
|
|
48
|
+
opacity: opacity.value,
|
|
49
|
+
transform: [{ scale: scale.value }],
|
|
50
|
+
}));
|
|
51
|
+
|
|
43
52
|
const onScreenNavigated = (pre: string, current: string) => {
|
|
44
53
|
if (!isBackgroundToForeground) {
|
|
45
54
|
const data: any = {
|
|
@@ -69,18 +78,8 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
|
|
|
69
78
|
useFocusEffect(
|
|
70
79
|
React.useCallback(
|
|
71
80
|
() => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
toValue: 1,
|
|
75
|
-
duration: 200,
|
|
76
|
-
useNativeDriver: true,
|
|
77
|
-
}),
|
|
78
|
-
Animated.timing(scale, {
|
|
79
|
-
toValue: 1,
|
|
80
|
-
duration: 200,
|
|
81
|
-
useNativeDriver: true,
|
|
82
|
-
}),
|
|
83
|
-
]).start();
|
|
81
|
+
opacity.value = withTiming(1, { duration: 200 });
|
|
82
|
+
scale.value = withTiming(1, { duration: 200 });
|
|
84
83
|
|
|
85
84
|
setTimeout(() => {
|
|
86
85
|
navigator?.maxApi?.getDataObserver?.(
|
|
@@ -96,18 +95,8 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
|
|
|
96
95
|
|
|
97
96
|
return () => {
|
|
98
97
|
if (navigation.getState().index !== route?.params.index) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
toValue: opacityValue,
|
|
102
|
-
duration: 200,
|
|
103
|
-
useNativeDriver: true,
|
|
104
|
-
}),
|
|
105
|
-
Animated.timing(scale, {
|
|
106
|
-
toValue: scaleValue,
|
|
107
|
-
duration: 200,
|
|
108
|
-
useNativeDriver: true,
|
|
109
|
-
}),
|
|
110
|
-
]).start();
|
|
98
|
+
opacity.value = withTiming(opacityValue, { duration: 200 });
|
|
99
|
+
scale.value = withTiming(scaleValue, { duration: 200 });
|
|
111
100
|
}
|
|
112
101
|
};
|
|
113
102
|
},
|
|
@@ -147,15 +136,7 @@ const TabScreen: React.FC<any> = ({ route, navigation }) => {
|
|
|
147
136
|
|
|
148
137
|
if (nested) {
|
|
149
138
|
return (
|
|
150
|
-
<Animated.View
|
|
151
|
-
style={[
|
|
152
|
-
styles.container,
|
|
153
|
-
{
|
|
154
|
-
opacity,
|
|
155
|
-
transform: [{ scale }],
|
|
156
|
-
},
|
|
157
|
-
]}
|
|
158
|
-
>
|
|
139
|
+
<Animated.View style={[styles.container, tabAnimatedStyle]}>
|
|
159
140
|
<Stack.Navigator
|
|
160
141
|
screenOptions={{
|
|
161
142
|
headerStyle: {
|
|
@@ -228,10 +209,15 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
228
209
|
const dimensions = useWindowDimensions();
|
|
229
210
|
const insets = useSafeAreaInsets();
|
|
230
211
|
const initialIndex = tabs.findIndex(i => i.name === initialRouteName);
|
|
231
|
-
const indicatorAnimated =
|
|
232
|
-
const activeIndex = useRef(initialIndex > 0 ? initialIndex : 0);
|
|
212
|
+
const indicatorAnimated = useSharedValue(0);
|
|
213
|
+
const activeIndex = React.useRef(initialIndex > 0 ? initialIndex : 0);
|
|
233
214
|
const itemWidth = dimensions.width / tabs.length;
|
|
234
215
|
|
|
216
|
+
const indicatorStyle = useAnimatedStyle(() => ({
|
|
217
|
+
width: itemWidth,
|
|
218
|
+
transform: [{ translateX: indicatorAnimated.value }],
|
|
219
|
+
}));
|
|
220
|
+
|
|
235
221
|
useLayoutEffect(() => {
|
|
236
222
|
navigation?.setOptions({
|
|
237
223
|
headerShown: false,
|
|
@@ -241,20 +227,16 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
241
227
|
}, [navigation]);
|
|
242
228
|
|
|
243
229
|
useEffect(() => {
|
|
244
|
-
|
|
245
|
-
toValue: itemWidth * activeIndex.current,
|
|
246
|
-
useNativeDriver: true,
|
|
230
|
+
indicatorAnimated.value = withTiming(itemWidth * activeIndex.current, {
|
|
247
231
|
duration: 200,
|
|
248
|
-
})
|
|
249
|
-
}, [itemWidth]);
|
|
232
|
+
});
|
|
233
|
+
}, [itemWidth, indicatorAnimated]);
|
|
250
234
|
|
|
251
235
|
const onFocus = (e: any) => {
|
|
252
236
|
activeIndex.current = tabs.findIndex(i => e.target.includes(i.name));
|
|
253
|
-
|
|
254
|
-
toValue: itemWidth * activeIndex.current,
|
|
255
|
-
useNativeDriver: true,
|
|
237
|
+
indicatorAnimated.value = withTiming(itemWidth * activeIndex.current, {
|
|
256
238
|
duration: 200,
|
|
257
|
-
})
|
|
239
|
+
});
|
|
258
240
|
};
|
|
259
241
|
|
|
260
242
|
const handler: {
|
|
@@ -294,13 +276,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
294
276
|
]}
|
|
295
277
|
>
|
|
296
278
|
<Animated.View
|
|
297
|
-
style={[
|
|
298
|
-
styles.indicatorContainer,
|
|
299
|
-
{
|
|
300
|
-
width: itemWidth,
|
|
301
|
-
transform: [{ translateX: indicatorAnimated.current }],
|
|
302
|
-
},
|
|
303
|
-
]}
|
|
279
|
+
style={[styles.indicatorContainer, indicatorStyle]}
|
|
304
280
|
>
|
|
305
281
|
<View
|
|
306
282
|
style={[
|
|
@@ -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>
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -8,13 +8,22 @@ import React, {
|
|
|
8
8
|
useState,
|
|
9
9
|
} from 'react';
|
|
10
10
|
import {
|
|
11
|
-
Animated,
|
|
12
11
|
PixelRatio,
|
|
13
12
|
TextInput,
|
|
14
13
|
TextInputFocusEvent,
|
|
15
14
|
TouchableOpacity,
|
|
16
15
|
View,
|
|
17
16
|
} from 'react-native';
|
|
17
|
+
import Animated, {
|
|
18
|
+
cancelAnimation,
|
|
19
|
+
Easing,
|
|
20
|
+
useAnimatedStyle,
|
|
21
|
+
useSharedValue,
|
|
22
|
+
withDelay,
|
|
23
|
+
withRepeat,
|
|
24
|
+
withSequence,
|
|
25
|
+
withTiming,
|
|
26
|
+
} from 'react-native-reanimated';
|
|
18
27
|
import { useComponentId } from '../Application';
|
|
19
28
|
import { Spacing, Styles } from '../Consts';
|
|
20
29
|
import { useScaleSize, Text } from '../Text';
|
|
@@ -32,37 +41,43 @@ import {
|
|
|
32
41
|
const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
33
42
|
const DURATION = 300;
|
|
34
43
|
const { theme } = useContext(ApplicationContext);
|
|
35
|
-
const opacity =
|
|
44
|
+
const opacity = useSharedValue(0);
|
|
36
45
|
|
|
37
46
|
useEffect(() => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
).start();
|
|
47
|
+
opacity.value = withRepeat(
|
|
48
|
+
withSequence(
|
|
49
|
+
withTiming(1, { duration: DURATION, easing: Easing.linear }),
|
|
50
|
+
withDelay(
|
|
51
|
+
DURATION * 2,
|
|
52
|
+
withTiming(0, { duration: DURATION, easing: Easing.linear }),
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
-1,
|
|
56
|
+
false,
|
|
57
|
+
);
|
|
58
|
+
return () => {
|
|
59
|
+
cancelAnimation(opacity);
|
|
60
|
+
};
|
|
53
61
|
}, [opacity]);
|
|
62
|
+
|
|
63
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
64
|
+
opacity: opacity.value,
|
|
65
|
+
}));
|
|
66
|
+
|
|
54
67
|
const spacingStyle = !isNaN(Number(length)) &&
|
|
55
68
|
index !== Number(length) - 1 && { marginRight: Spacing.L };
|
|
56
69
|
|
|
57
70
|
return (
|
|
58
71
|
<View style={[Styles.rowCenter, spacingStyle]}>
|
|
59
72
|
<Animated.View
|
|
60
|
-
style={
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
style={[
|
|
74
|
+
{
|
|
75
|
+
height: useScaleSize(12),
|
|
76
|
+
width: 1,
|
|
77
|
+
backgroundColor: theme.colors.primary,
|
|
78
|
+
},
|
|
79
|
+
animatedStyle,
|
|
80
|
+
]}
|
|
66
81
|
/>
|
|
67
82
|
<Text color={theme.colors.text.hint} typography={'body_default_regular'}>
|
|
68
83
|
-
|
package/Loader/ProgressBar.tsx
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import React, { FC, useContext, useEffect
|
|
2
|
-
import {
|
|
1
|
+
import React, { FC, useContext, useEffect } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
useSharedValue,
|
|
6
|
+
withTiming,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
3
8
|
import styles from './styles';
|
|
4
9
|
import { ProgressBarProps } from './types';
|
|
5
10
|
import { ApplicationContext } from '../Context';
|
|
@@ -7,20 +12,15 @@ import { Radius } from '../Consts';
|
|
|
7
12
|
|
|
8
13
|
const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
9
14
|
const { theme } = useContext(ApplicationContext);
|
|
10
|
-
const animation =
|
|
15
|
+
const animation = useSharedValue(0);
|
|
11
16
|
|
|
12
17
|
useEffect(() => {
|
|
13
|
-
|
|
14
|
-
toValue: percent,
|
|
15
|
-
duration: 200,
|
|
16
|
-
useNativeDriver: false,
|
|
17
|
-
}).start();
|
|
18
|
+
animation.value = withTiming(percent, { duration: 200 });
|
|
18
19
|
}, [percent, animation]);
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
21
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
22
|
+
width: `${Math.min(Math.max(animation.value, 0), 100)}%`,
|
|
23
|
+
}));
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<View
|
|
@@ -31,12 +31,14 @@ const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
|
31
31
|
]}
|
|
32
32
|
>
|
|
33
33
|
<Animated.View
|
|
34
|
-
style={
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
style={[
|
|
35
|
+
{
|
|
36
|
+
height: 4,
|
|
37
|
+
borderRadius: Radius.XXS,
|
|
38
|
+
backgroundColor: theme.colors.primary,
|
|
39
|
+
},
|
|
40
|
+
animatedStyle,
|
|
41
|
+
]}
|
|
40
42
|
/>
|
|
41
43
|
</View>
|
|
42
44
|
);
|
package/Pagination/Dot.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { FC, useContext } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { View } from 'react-native';
|
|
3
3
|
import styles from './styles';
|
|
4
4
|
import { DotProps } from './types';
|
|
5
5
|
import { ApplicationContext } from '../Context';
|
|
@@ -13,7 +13,7 @@ const Dot: FC<DotProps> = ({ active, style }) => {
|
|
|
13
13
|
{ backgroundColor: theme.colors.background.pressed },
|
|
14
14
|
];
|
|
15
15
|
|
|
16
|
-
return <
|
|
16
|
+
return <View style={[style, dotStyle]} />;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export default Dot;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import React, { FC, useContext,
|
|
2
|
-
import {
|
|
1
|
+
import React, { FC, useContext, useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
Extrapolation,
|
|
5
|
+
interpolate,
|
|
6
|
+
useAnimatedScrollHandler,
|
|
7
|
+
useAnimatedStyle,
|
|
8
|
+
useSharedValue,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
3
10
|
import { ScrollIndicatorProps } from './types';
|
|
4
11
|
import styles from './styles';
|
|
5
12
|
import { ApplicationContext, MiniAppContext } from '../Context';
|
|
@@ -13,40 +20,37 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
|
|
|
13
20
|
}) => {
|
|
14
21
|
const { theme } = useContext(ApplicationContext);
|
|
15
22
|
const context = useContext<any>(MiniAppContext);
|
|
16
|
-
const left =
|
|
23
|
+
const left = useSharedValue(0);
|
|
17
24
|
const [scrollViewWidth, setScrollViewWidth] = useState(0);
|
|
18
25
|
const [scrollContentWidth, setScrollContentWidth] = useState(0);
|
|
19
26
|
|
|
20
27
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
21
28
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const onScroll = useAnimatedScrollHandler({
|
|
30
|
+
onScroll: (event) => {
|
|
31
|
+
left.value = event.contentOffset.x;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const indicatorStyle = useAnimatedStyle(() => {
|
|
36
|
+
if (!scrollViewWidth || !scrollContentWidth) {
|
|
37
|
+
return {};
|
|
30
38
|
}
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
const value = interpolate(
|
|
40
|
+
left.value,
|
|
41
|
+
[0, scrollContentWidth - scrollViewWidth],
|
|
42
|
+
[0, INDICATOR_CONTAINER_WIDTH - INDICATOR_WIDTH],
|
|
43
|
+
Extrapolation.CLAMP,
|
|
44
|
+
);
|
|
45
|
+
return { transform: [{ translateX: value }] };
|
|
46
|
+
});
|
|
33
47
|
|
|
34
48
|
const renderScrollView = () => {
|
|
35
49
|
return (
|
|
36
50
|
<Animated.ScrollView
|
|
37
|
-
ref={scrollViewRef}
|
|
38
|
-
onScroll={
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
nativeEvent: {
|
|
42
|
-
contentOffset: {
|
|
43
|
-
x: left,
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
{ useNativeDriver: true },
|
|
49
|
-
)}
|
|
51
|
+
ref={scrollViewRef as any}
|
|
52
|
+
onScroll={onScroll}
|
|
53
|
+
scrollEventThrottle={16}
|
|
50
54
|
alwaysBounceHorizontal={false}
|
|
51
55
|
showsHorizontalScrollIndicator={false}
|
|
52
56
|
horizontal
|
|
@@ -76,7 +80,7 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
|
|
|
76
80
|
{
|
|
77
81
|
backgroundColor: theme.colors.primary,
|
|
78
82
|
},
|
|
79
|
-
|
|
83
|
+
indicatorStyle,
|
|
80
84
|
]}
|
|
81
85
|
/>
|
|
82
86
|
</View>
|
package/Skeleton/index.tsx
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import React, { useContext, useEffect,
|
|
1
|
+
import React, { useContext, useEffect, useState } from 'react';
|
|
2
2
|
import {
|
|
3
|
-
Animated,
|
|
4
|
-
Easing,
|
|
5
3
|
LayoutAnimation,
|
|
6
4
|
Platform,
|
|
7
5
|
StyleSheet,
|
|
8
6
|
UIManager,
|
|
9
7
|
View,
|
|
10
8
|
} from 'react-native';
|
|
9
|
+
import Animated, {
|
|
10
|
+
cancelAnimation,
|
|
11
|
+
Easing,
|
|
12
|
+
interpolate,
|
|
13
|
+
useAnimatedStyle,
|
|
14
|
+
useSharedValue,
|
|
15
|
+
withRepeat,
|
|
16
|
+
withTiming,
|
|
17
|
+
} from 'react-native-reanimated';
|
|
11
18
|
import LinearGradient from 'react-native-linear-gradient';
|
|
12
19
|
import { SkeletonTypes } from './types';
|
|
13
20
|
import { Colors, Styles } from '../Consts';
|
|
@@ -20,32 +27,31 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
|
|
|
20
27
|
const PRIMARY_COLOR = Colors.black_05;
|
|
21
28
|
const HIGHLIGHT_COLOR1 = Colors.black_05;
|
|
22
29
|
const HIGHLIGHT_COLOR2 = Colors.black_03;
|
|
23
|
-
const
|
|
30
|
+
const progress = useSharedValue(0);
|
|
24
31
|
|
|
25
32
|
const shimmerColors = [HIGHLIGHT_COLOR1, HIGHLIGHT_COLOR2, HIGHLIGHT_COLOR1];
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return Animated.loop(
|
|
32
|
-
Animated.timing(beginShimmerPosition, {
|
|
33
|
-
toValue: 1,
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
progress.value = 0;
|
|
36
|
+
progress.value = withRepeat(
|
|
37
|
+
withTiming(1, {
|
|
34
38
|
duration: 1000,
|
|
35
39
|
easing: Easing.linear,
|
|
36
|
-
useNativeDriver: Platform.OS !== 'web',
|
|
37
40
|
}),
|
|
41
|
+
-1,
|
|
42
|
+
false,
|
|
38
43
|
);
|
|
39
|
-
}, [beginShimmerPosition]);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
animatedValue.start();
|
|
43
44
|
screen?.onLoading?.(true);
|
|
44
45
|
return () => {
|
|
45
|
-
|
|
46
|
+
cancelAnimation(progress);
|
|
46
47
|
screen?.onLoading?.(false);
|
|
47
48
|
};
|
|
48
|
-
}, [
|
|
49
|
+
}, [progress, screen]);
|
|
50
|
+
|
|
51
|
+
const shimmerStyle = useAnimatedStyle(() => {
|
|
52
|
+
const translateX = interpolate(progress.value, [0, 1], [-width, width]);
|
|
53
|
+
return { transform: [{ translateX }] };
|
|
54
|
+
});
|
|
49
55
|
|
|
50
56
|
const onLayout = (newWidth: number) => {
|
|
51
57
|
if (newWidth !== width) {
|
|
@@ -60,11 +66,13 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
|
|
|
60
66
|
style={[Styles.flex, { backgroundColor: PRIMARY_COLOR }]}
|
|
61
67
|
>
|
|
62
68
|
<Animated.View
|
|
63
|
-
style={
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
style={[
|
|
70
|
+
{
|
|
71
|
+
width: '60%',
|
|
72
|
+
height: '100%',
|
|
73
|
+
},
|
|
74
|
+
shimmerStyle,
|
|
75
|
+
]}
|
|
68
76
|
>
|
|
69
77
|
<LinearGradient
|
|
70
78
|
style={[StyleSheet.absoluteFill]}
|