@momo-kits/foundation 0.162.2-sp.2 → 0.162.2-test.10
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 +55 -31
- package/Application/Components/BackgroundImageView.tsx +6 -22
- package/Application/Components/HeaderAnimated.tsx +29 -29
- package/Application/Components/HeaderBackground.tsx +26 -20
- package/Application/Components/HeaderExtendHeader.tsx +94 -115
- package/Application/Components/HeaderTitle.tsx +10 -43
- package/Application/Components/SearchHeader.tsx +21 -13
- package/Application/NavigationContainer.tsx +27 -2
- package/Application/ScaleSizeProvider.tsx +5 -3
- package/Application/WidgetContainer.tsx +1 -1
- package/Application/types.ts +5 -5
- package/Application/utils.tsx +2 -42
- package/Badge/BadgeDotAnimation.tsx +71 -53
- package/Button/index.tsx +4 -1
- package/Context/index.ts +6 -3
- package/Input/InputOTP.tsx +23 -38
- package/Layout/FloatingButton.tsx +59 -64
- package/Layout/Screen.tsx +20 -66
- package/Loader/ProgressBar.tsx +18 -20
- package/Pagination/Dot.tsx +2 -2
- package/Pagination/PaginationScroll.tsx +27 -31
- package/Skeleton/index.tsx +24 -32
- package/Text/utils.ts +15 -5
- package/package.json +1 -1
|
@@ -1,75 +1,93 @@
|
|
|
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';
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, View } from 'react-native';
|
|
12
3
|
import { BadgeDotProps } from './types';
|
|
13
4
|
import styles from './styles';
|
|
14
5
|
|
|
15
6
|
const DURATION = 500;
|
|
16
7
|
|
|
17
8
|
const BadgeDotAnimation = ({ size, style }: BadgeDotProps) => {
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
9
|
+
// Refs for animated values
|
|
10
|
+
const scaleAnim = useRef(new Animated.Value(1)).current;
|
|
11
|
+
const waveScaleAnim = useRef(new Animated.Value(1)).current;
|
|
12
|
+
const waveOpacityAnim = useRef(new Animated.Value(0)).current;
|
|
21
13
|
|
|
22
14
|
const dotStyle =
|
|
23
15
|
size === 'small' ? styles.dotAnimationSmall : styles.dotAnimation;
|
|
24
16
|
const waveStyle = size === 'small' ? styles.waveSmall : styles.wave;
|
|
25
17
|
|
|
26
18
|
useEffect(() => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
19
|
+
// Infinite loop animation for the scale and wave effect
|
|
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
|
+
]),
|
|
52
62
|
);
|
|
63
|
+
animation.start();
|
|
64
|
+
|
|
53
65
|
return () => {
|
|
54
|
-
|
|
55
|
-
cancelAnimation(waveScaleAnim);
|
|
56
|
-
cancelAnimation(waveOpacityAnim);
|
|
66
|
+
animation.stop();
|
|
57
67
|
};
|
|
58
68
|
}, [scaleAnim, waveOpacityAnim, waveScaleAnim]);
|
|
59
69
|
|
|
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
|
-
|
|
69
70
|
return (
|
|
70
71
|
<View style={[styles.dotAnimationContainer, style]}>
|
|
71
|
-
|
|
72
|
-
<Animated.View
|
|
72
|
+
{/* Wave Animation */}
|
|
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
|
+
/>
|
|
73
91
|
</View>
|
|
74
92
|
);
|
|
75
93
|
};
|
package/Button/index.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { FC, useContext, useRef } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
Animated,
|
|
3
4
|
StyleSheet,
|
|
4
5
|
TouchableOpacity,
|
|
5
6
|
TouchableOpacityProps,
|
|
@@ -28,6 +29,8 @@ import Reanimated, {
|
|
|
28
29
|
withTiming,
|
|
29
30
|
} from 'react-native-reanimated';
|
|
30
31
|
|
|
32
|
+
const AnimationLinear = Animated.createAnimatedComponent(LinearGradient);
|
|
33
|
+
|
|
31
34
|
export interface ButtonProps extends TouchableOpacityProps {
|
|
32
35
|
/**
|
|
33
36
|
* Defines the visual style of the button.
|
|
@@ -356,7 +359,7 @@ const Button: FC<ButtonProps> = ({
|
|
|
356
359
|
{renderTitle()}
|
|
357
360
|
{renderIcon('right')}
|
|
358
361
|
{gradientPros && (
|
|
359
|
-
<
|
|
362
|
+
<AnimationLinear {...gradientPros} style={styles.gradientView} />
|
|
360
363
|
)}
|
|
361
364
|
{gradientPros && <View style={styles.strokeView} />}
|
|
362
365
|
</Reanimated.View>
|
package/Context/index.ts
CHANGED
|
@@ -9,9 +9,12 @@ const MiniAppContext = (Platform as any).MiniAppContext ?? Context;
|
|
|
9
9
|
const ScreenContext = (Platform as any).ScreenContext ?? Context;
|
|
10
10
|
const ComponentContext = (Platform as any).ComponentContext ?? Context;
|
|
11
11
|
const SkeletonContext = createContext({ loading: false });
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
// Single source of truth: holds both the OS flag and the user-adjusted rate.
|
|
13
|
+
export type FontScaleConfig = {
|
|
14
|
+
useOSFontScale?: boolean;
|
|
15
|
+
userScaleRate?: number;
|
|
16
|
+
};
|
|
17
|
+
const ScaleSizeContext = createContext<FontScaleConfig>({});
|
|
15
18
|
const TrackingScopeContext = createContext<{ scopeName?: string }>({
|
|
16
19
|
scopeName: undefined,
|
|
17
20
|
});
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -8,22 +8,13 @@ import React, {
|
|
|
8
8
|
useState,
|
|
9
9
|
} from 'react';
|
|
10
10
|
import {
|
|
11
|
+
Animated,
|
|
11
12
|
PixelRatio,
|
|
12
13
|
TextInput,
|
|
13
14
|
TextInputFocusEvent,
|
|
14
15
|
TouchableOpacity,
|
|
15
16
|
View,
|
|
16
17
|
} 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';
|
|
27
18
|
import { useComponentId } from '../Application';
|
|
28
19
|
import { Spacing, Styles } from '../Consts';
|
|
29
20
|
import { useScaleSize, Text } from '../Text';
|
|
@@ -41,43 +32,37 @@ import {
|
|
|
41
32
|
const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
42
33
|
const DURATION = 300;
|
|
43
34
|
const { theme } = useContext(ApplicationContext);
|
|
44
|
-
const opacity =
|
|
35
|
+
const opacity = useRef(new Animated.Value(0)).current;
|
|
45
36
|
|
|
46
37
|
useEffect(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
DURATION
|
|
52
|
-
|
|
53
|
-
),
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
38
|
+
Animated.loop(
|
|
39
|
+
Animated.sequence([
|
|
40
|
+
Animated.timing(opacity, {
|
|
41
|
+
toValue: 1,
|
|
42
|
+
duration: DURATION,
|
|
43
|
+
useNativeDriver: true,
|
|
44
|
+
}),
|
|
45
|
+
Animated.delay(DURATION * 2),
|
|
46
|
+
Animated.timing(opacity, {
|
|
47
|
+
toValue: 0,
|
|
48
|
+
duration: DURATION,
|
|
49
|
+
useNativeDriver: true,
|
|
50
|
+
}),
|
|
51
|
+
]),
|
|
52
|
+
).start();
|
|
61
53
|
}, [opacity]);
|
|
62
|
-
|
|
63
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
64
|
-
opacity: opacity.value,
|
|
65
|
-
}));
|
|
66
|
-
|
|
67
54
|
const spacingStyle = !isNaN(Number(length)) &&
|
|
68
55
|
index !== Number(length) - 1 && { marginRight: Spacing.L };
|
|
69
56
|
|
|
70
57
|
return (
|
|
71
58
|
<View style={[Styles.rowCenter, spacingStyle]}>
|
|
72
59
|
<Animated.View
|
|
73
|
-
style={
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
animatedStyle,
|
|
80
|
-
]}
|
|
60
|
+
style={{
|
|
61
|
+
height: useScaleSize(12),
|
|
62
|
+
width: 1,
|
|
63
|
+
backgroundColor: theme.colors.primary,
|
|
64
|
+
opacity,
|
|
65
|
+
}}
|
|
81
66
|
/>
|
|
82
67
|
<Text color={theme.colors.text.hint} typography={'body_default_regular'}>
|
|
83
68
|
-
|
|
@@ -1,24 +1,20 @@
|
|
|
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
|
-
} from 'react-native-reanimated';
|
|
15
15
|
import { ApplicationContext } from '../Context';
|
|
16
16
|
import { Icon } from '../Icon';
|
|
17
17
|
import { useScaleSize } from '../Text';
|
|
18
|
-
import {
|
|
19
|
-
AnimatedCompatValue,
|
|
20
|
-
useNormalizedSharedValue,
|
|
21
|
-
} from '../Application/utils';
|
|
22
18
|
|
|
23
19
|
export interface FloatingButtonProps {
|
|
24
20
|
label?: string;
|
|
@@ -27,7 +23,7 @@ export interface FloatingButtonProps {
|
|
|
27
23
|
icon?: string;
|
|
28
24
|
iconColor?: string;
|
|
29
25
|
size?: 'small' | 'large';
|
|
30
|
-
animatedValue?:
|
|
26
|
+
animatedValue?: Animated.Value;
|
|
31
27
|
bottom?: number;
|
|
32
28
|
renderComponent?: () => React.ReactNode;
|
|
33
29
|
}
|
|
@@ -44,71 +40,70 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
44
40
|
bottom = 12,
|
|
45
41
|
}: FloatingButtonProps) => {
|
|
46
42
|
const { theme } = useContext(ApplicationContext);
|
|
47
|
-
const enabled = animatedValue != null;
|
|
48
|
-
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
49
43
|
const scaledFontSize = useScaleSize(16);
|
|
50
44
|
const scaledLineHeight = useScaleSize(22);
|
|
51
|
-
const maxWidth =
|
|
45
|
+
const maxWidth = useRef(0);
|
|
52
46
|
const minWidth = size === 'small' ? 36 : 48;
|
|
53
|
-
const opacityAnimated =
|
|
54
|
-
const widthAnimated =
|
|
55
|
-
const lastOffset =
|
|
56
|
-
const lastDirection =
|
|
57
|
-
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);
|
|
58
52
|
|
|
59
53
|
useEffect(() => {
|
|
60
54
|
if (!label) return;
|
|
61
|
-
opacityAnimated.value = withTiming(1, { duration: 100 });
|
|
62
|
-
}, [label, opacityAnimated]);
|
|
63
55
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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;
|
|
74
68
|
if (direction === 'down') {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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));
|
|
83
79
|
} else {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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));
|
|
92
90
|
}
|
|
93
91
|
}
|
|
94
92
|
}
|
|
95
|
-
}
|
|
96
|
-
[label, enabled, minWidth],
|
|
97
|
-
);
|
|
93
|
+
});
|
|
98
94
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}));
|
|
95
|
+
return () => {
|
|
96
|
+
if (listener) {
|
|
97
|
+
animatedValue?.removeListener(listener);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}, [animatedValue, label, minWidth, opacityAnimated, widthAnimated]);
|
|
106
101
|
|
|
107
102
|
const handleLayout = (event: LayoutChangeEvent) => {
|
|
108
103
|
const layout = event.nativeEvent.layout;
|
|
109
|
-
if (widthAnimated
|
|
110
|
-
maxWidth.
|
|
111
|
-
|
|
104
|
+
if (widthAnimated) return;
|
|
105
|
+
maxWidth.current = layout.width;
|
|
106
|
+
setWidthAnimated(new Animated.Value(layout.width));
|
|
112
107
|
};
|
|
113
108
|
|
|
114
109
|
if (renderComponent) {
|
|
@@ -137,11 +132,11 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
137
132
|
{
|
|
138
133
|
right: position === 'right' ? 12 : undefined,
|
|
139
134
|
alignSelf: position === 'center' ? 'center' : 'flex-end',
|
|
135
|
+
width: widthAnimated,
|
|
140
136
|
height: size === 'small' ? 36 : 48,
|
|
141
137
|
backgroundColor: theme.colors.primary,
|
|
142
138
|
bottom,
|
|
143
139
|
},
|
|
144
|
-
containerStyle,
|
|
145
140
|
]}
|
|
146
141
|
>
|
|
147
142
|
<TouchableOpacity
|
|
@@ -161,9 +156,9 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
161
156
|
{
|
|
162
157
|
fontSize: scaledFontSize,
|
|
163
158
|
lineHeight: scaledLineHeight,
|
|
159
|
+
opacity: opacityAnimated,
|
|
164
160
|
color: 'white',
|
|
165
161
|
},
|
|
166
|
-
labelStyle,
|
|
167
162
|
]}
|
|
168
163
|
numberOfLines={1}
|
|
169
164
|
>
|
package/Layout/Screen.tsx
CHANGED
|
@@ -25,10 +25,6 @@ import {
|
|
|
25
25
|
View,
|
|
26
26
|
ViewProps,
|
|
27
27
|
} from 'react-native';
|
|
28
|
-
import {
|
|
29
|
-
useSharedValue,
|
|
30
|
-
withTiming,
|
|
31
|
-
} from 'react-native-reanimated';
|
|
32
28
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
33
29
|
import { ApplicationContext, ScreenContext } from '../Context';
|
|
34
30
|
import Navigation from '../Application/Navigation';
|
|
@@ -140,8 +136,7 @@ export interface ScreenProps extends ViewProps {
|
|
|
140
136
|
inputSearchRef?: Ref<InputRef>;
|
|
141
137
|
|
|
142
138
|
/**
|
|
143
|
-
* Optional.
|
|
144
|
-
* Internally, Screen mirrors it into a reanimated shared value.
|
|
139
|
+
* Optional. Animated value for header.
|
|
145
140
|
*/
|
|
146
141
|
animatedValue?: Animated.Value;
|
|
147
142
|
|
|
@@ -200,40 +195,9 @@ const Screen = forwardRef(
|
|
|
200
195
|
const screen: any = useContext(ScreenContext);
|
|
201
196
|
const insets = useSafeAreaInsets();
|
|
202
197
|
const heightHeader = useHeaderHeight();
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
* Dual animation source:
|
|
206
|
-
* - `rnValue` is a react-native `Animated.Value` driven natively by the
|
|
207
|
-
* scroll event. It is what legacy consumers receive (`animatedValue`).
|
|
208
|
-
* - `sharedValue` is a reanimated `SharedValue` consumed by every internal
|
|
209
|
-
* foundation component (and offered to modern consumers). It is kept in
|
|
210
|
-
* sync from the scroll `listener` (see below), which fires on the JS
|
|
211
|
-
* thread even when the scroll uses the native driver.
|
|
212
|
-
* If a legacy value is driven outside of Screen, its listener also
|
|
213
|
-
* mirrors JS-driven updates into the shared value.
|
|
214
|
-
* Consumers pass only `Animated.Value`; the reanimated shared value stays
|
|
215
|
-
* an internal implementation detail.
|
|
216
|
-
*/
|
|
217
|
-
const rnValue = useRef<Animated.Value>(
|
|
218
|
-
customAnimatedValue ?? new Animated.Value(0),
|
|
198
|
+
const animatedValue = useRef<Animated.Value>(
|
|
199
|
+
customAnimatedValue || new Animated.Value(0),
|
|
219
200
|
);
|
|
220
|
-
const internalShared = useSharedValue(0);
|
|
221
|
-
const sharedValue = internalShared;
|
|
222
|
-
|
|
223
|
-
useEffect(() => {
|
|
224
|
-
if (!customAnimatedValue) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const listenerId = customAnimatedValue.addListener(({ value }) => {
|
|
229
|
-
internalShared.value = value;
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
return () => {
|
|
233
|
-
customAnimatedValue.removeListener(listenerId);
|
|
234
|
-
};
|
|
235
|
-
}, [customAnimatedValue, internalShared]);
|
|
236
|
-
|
|
237
201
|
const currentTint = useRef<string | undefined>(undefined);
|
|
238
202
|
const isTab = navigation?.instance?.getState?.()?.type === 'tab';
|
|
239
203
|
|
|
@@ -284,8 +248,9 @@ const Screen = forwardRef(
|
|
|
284
248
|
interpolate={{
|
|
285
249
|
inputRange: [0, 50],
|
|
286
250
|
outputRange: [1, 0],
|
|
251
|
+
extrapolate: 'clamp',
|
|
287
252
|
}}
|
|
288
|
-
animatedValue={
|
|
253
|
+
animatedValue={animatedValue.current}
|
|
289
254
|
/>
|
|
290
255
|
),
|
|
291
256
|
};
|
|
@@ -300,7 +265,7 @@ const Screen = forwardRef(
|
|
|
300
265
|
headerBackground: (props: any) => (
|
|
301
266
|
<HeaderBackground
|
|
302
267
|
{...props}
|
|
303
|
-
animatedValue={
|
|
268
|
+
animatedValue={animatedValue.current}
|
|
304
269
|
useShadowHeader={useShadowHeader}
|
|
305
270
|
headerBackground={headerBackground}
|
|
306
271
|
gradientColor={gradientColor}
|
|
@@ -319,8 +284,9 @@ const Screen = forwardRef(
|
|
|
319
284
|
interpolate={{
|
|
320
285
|
inputRange: [0, 50],
|
|
321
286
|
outputRange: [1, 0],
|
|
287
|
+
extrapolate: 'clamp',
|
|
322
288
|
}}
|
|
323
|
-
animatedValue={
|
|
289
|
+
animatedValue={animatedValue.current}
|
|
324
290
|
/>
|
|
325
291
|
),
|
|
326
292
|
};
|
|
@@ -334,7 +300,6 @@ const Screen = forwardRef(
|
|
|
334
300
|
headerBackground,
|
|
335
301
|
inputSearchProps,
|
|
336
302
|
navigation?.instance,
|
|
337
|
-
sharedValue,
|
|
338
303
|
useShadowHeader,
|
|
339
304
|
],
|
|
340
305
|
);
|
|
@@ -356,7 +321,7 @@ const Screen = forwardRef(
|
|
|
356
321
|
headerBackground: (props: any) => (
|
|
357
322
|
<HeaderBackground
|
|
358
323
|
{...props}
|
|
359
|
-
animatedValue={
|
|
324
|
+
animatedValue={animatedValue.current}
|
|
360
325
|
useGradient={false}
|
|
361
326
|
useShadowHeader={useShadowHeader}
|
|
362
327
|
headerBackground={headerBackground}
|
|
@@ -377,13 +342,7 @@ const Screen = forwardRef(
|
|
|
377
342
|
|
|
378
343
|
navigation?.instance?.setOptions(options);
|
|
379
344
|
},
|
|
380
|
-
[
|
|
381
|
-
gradientColor,
|
|
382
|
-
headerBackground,
|
|
383
|
-
navigation?.instance,
|
|
384
|
-
sharedValue,
|
|
385
|
-
useShadowHeader,
|
|
386
|
-
],
|
|
345
|
+
[gradientColor, headerBackground, navigation?.instance, useShadowHeader],
|
|
387
346
|
);
|
|
388
347
|
|
|
389
348
|
/**
|
|
@@ -400,14 +359,15 @@ const Screen = forwardRef(
|
|
|
400
359
|
interpolate={{
|
|
401
360
|
inputRange: [0, 50],
|
|
402
361
|
outputRange: [1, 0],
|
|
362
|
+
extrapolate: 'clamp',
|
|
403
363
|
}}
|
|
404
|
-
animatedValue={
|
|
364
|
+
animatedValue={animatedValue.current}
|
|
405
365
|
/>
|
|
406
366
|
),
|
|
407
367
|
};
|
|
408
368
|
|
|
409
369
|
navigation?.instance?.setOptions(options);
|
|
410
|
-
}, [navigation?.instance
|
|
370
|
+
}, [navigation?.instance]);
|
|
411
371
|
|
|
412
372
|
/**
|
|
413
373
|
* export search header
|
|
@@ -430,7 +390,7 @@ const Screen = forwardRef(
|
|
|
430
390
|
headerLeft: (props: any) =>
|
|
431
391
|
params?.hiddenBack ? null : <HeaderLeft {...props} />,
|
|
432
392
|
headerTitle: () => (
|
|
433
|
-
<SearchHeader {...params} animatedValue={
|
|
393
|
+
<SearchHeader {...params} animatedValue={animatedValue.current} />
|
|
434
394
|
),
|
|
435
395
|
};
|
|
436
396
|
|
|
@@ -490,18 +450,16 @@ const Screen = forwardRef(
|
|
|
490
450
|
[
|
|
491
451
|
{
|
|
492
452
|
nativeEvent: {
|
|
493
|
-
contentOffset: { y:
|
|
453
|
+
contentOffset: { y: animatedValue.current as Animated.Value },
|
|
494
454
|
},
|
|
495
455
|
},
|
|
496
456
|
],
|
|
497
457
|
{
|
|
498
458
|
useNativeDriver: true,
|
|
499
459
|
listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
500
|
-
const offsetY = e.nativeEvent.contentOffset.y;
|
|
501
|
-
// keep the reanimated source in sync for the internal components.
|
|
502
|
-
sharedValue.value = offsetY;
|
|
503
460
|
scrollViewProps?.onScroll?.(e);
|
|
504
461
|
if (animatedHeader) {
|
|
462
|
+
const offsetY = e.nativeEvent.contentOffset.y;
|
|
505
463
|
let color = animatedHeader?.headerTintColor ?? Colors.black_17;
|
|
506
464
|
if (offsetY > 50) {
|
|
507
465
|
color = Colors.black_17;
|
|
@@ -531,12 +489,11 @@ const Screen = forwardRef(
|
|
|
531
489
|
const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
532
490
|
const offsetY = e.nativeEvent.contentOffset.y;
|
|
533
491
|
if (inputSearchProps && offsetY < 100 && offsetY > 0) {
|
|
534
|
-
Animated.timing(
|
|
492
|
+
Animated.timing(animatedValue.current, {
|
|
535
493
|
toValue: 0,
|
|
536
494
|
useNativeDriver: true,
|
|
537
495
|
duration: 300,
|
|
538
496
|
}).start();
|
|
539
|
-
sharedValue.value = withTiming(0, { duration: 300 });
|
|
540
497
|
ref?.scrollTo?.({ y: 0, animated: true });
|
|
541
498
|
}
|
|
542
499
|
scrollViewProps?.onScrollEndDrag?.(e);
|
|
@@ -552,10 +509,7 @@ const Screen = forwardRef(
|
|
|
552
509
|
style={[styles.screenBanner, { maxHeight: 210 + layoutOffset }]}
|
|
553
510
|
>
|
|
554
511
|
{animatedHeader?.component({
|
|
555
|
-
|
|
556
|
-
// modern consumers read `sharedValue` (SharedValue).
|
|
557
|
-
animatedValue: rnValue.current,
|
|
558
|
-
sharedValue: sharedValue,
|
|
512
|
+
animatedValue: animatedValue.current,
|
|
559
513
|
})}
|
|
560
514
|
</View>
|
|
561
515
|
);
|
|
@@ -622,7 +576,7 @@ const Screen = forwardRef(
|
|
|
622
576
|
headerType={headerType}
|
|
623
577
|
heightHeader={heightHeader}
|
|
624
578
|
headerRightWidth={headerRightWidth}
|
|
625
|
-
animatedValue={
|
|
579
|
+
animatedValue={animatedValue.current}
|
|
626
580
|
inputSearchProps={inputSearchProps}
|
|
627
581
|
navigation={navigation}
|
|
628
582
|
inputSearchRef={inputSearchRef}
|
|
@@ -660,7 +614,7 @@ const Screen = forwardRef(
|
|
|
660
614
|
<View>
|
|
661
615
|
<FloatingButton
|
|
662
616
|
{...floatingButtonProps}
|
|
663
|
-
animatedValue={
|
|
617
|
+
animatedValue={animatedValue.current}
|
|
664
618
|
bottom={
|
|
665
619
|
Footer || isTab ? 12 : Math.min(insets.bottom, 21) + Spacing.S
|
|
666
620
|
}
|