@momo-kits/foundation 0.162.2-test.8 → 0.162.3-fontscale.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 +5 -4
- package/Application/BottomTab/BottomTabBar.tsx +15 -15
- package/Application/BottomTab/index.tsx +32 -56
- package/Application/Components/BackgroundImageView.tsx +22 -6
- package/Application/Components/HeaderAnimated.tsx +29 -29
- package/Application/Components/HeaderBackground.tsx +20 -26
- package/Application/Components/HeaderExtendHeader.tsx +115 -94
- package/Application/Components/HeaderLeft.tsx +1 -1
- package/Application/Components/HeaderTitle.tsx +43 -10
- package/Application/Components/SearchHeader.tsx +13 -21
- package/Application/NavigationContainer.tsx +8 -27
- package/Application/ScaleSizeProvider.tsx +3 -5
- package/Application/WidgetContainer.tsx +1 -1
- package/Application/types.ts +5 -5
- package/Application/utils.tsx +42 -2
- package/Badge/BadgeDotAnimation.tsx +53 -71
- package/Button/index.tsx +1 -4
- package/Context/index.ts +3 -6
- package/Input/Input.tsx +12 -1
- package/Input/InputOTP.tsx +38 -23
- package/Input/InputPhoneNumber.tsx +12 -1
- package/Layout/FloatingButton.tsx +64 -59
- package/Layout/Screen.tsx +75 -21
- 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 +6 -11
- package/package.json +1 -1
|
@@ -1,7 +1,12 @@
|
|
|
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
|
+
} from 'react-native-reanimated';
|
|
3
9
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
4
|
-
import { Animated, Dimensions, Platform, StyleSheet, View } from 'react-native';
|
|
5
10
|
import { HeaderType } from '../../Layout/types';
|
|
6
11
|
import { InputRef, InputSearch } from '../../Input';
|
|
7
12
|
import Navigation from '../Navigation';
|
|
@@ -9,20 +14,19 @@ import { Colors, Radius, Spacing } from '../../Consts';
|
|
|
9
14
|
import { Image } from '../../Image';
|
|
10
15
|
import { SearchHeaderProps } from '../types';
|
|
11
16
|
import BackgroundImageView from './BackgroundImageView';
|
|
17
|
+
import {
|
|
18
|
+
AnimatedCompatValue,
|
|
19
|
+
useNormalizedSharedValue,
|
|
20
|
+
} from '../utils';
|
|
12
21
|
|
|
13
22
|
const SCREEN_PADDING = 12;
|
|
14
23
|
const BACK_WIDTH = 28;
|
|
15
24
|
|
|
16
25
|
const { width: screenWidth } = Dimensions.get('window');
|
|
17
|
-
const LinearGradientAnimated = Animated.createAnimatedComponent(LinearGradient);
|
|
18
26
|
|
|
19
|
-
/**
|
|
20
|
-
* Header extended with background image
|
|
21
|
-
* @constructor
|
|
22
|
-
*/
|
|
23
27
|
const HeaderExtendHeader: React.FC<{
|
|
24
28
|
headerType?: HeaderType;
|
|
25
|
-
animatedValue:
|
|
29
|
+
animatedValue: AnimatedCompatValue;
|
|
26
30
|
heightHeader: number;
|
|
27
31
|
headerRightWidth: number;
|
|
28
32
|
inputSearchProps?: SearchHeaderProps;
|
|
@@ -38,118 +42,131 @@ const HeaderExtendHeader: React.FC<{
|
|
|
38
42
|
headerRightWidth = 73,
|
|
39
43
|
inputSearchProps,
|
|
40
44
|
inputSearchRef,
|
|
41
|
-
useShadowHeader = true,
|
|
45
|
+
useShadowHeader: useShadowHeaderProp = true,
|
|
42
46
|
gradientColor: customGradientColor,
|
|
43
47
|
headerBackground: customBackground,
|
|
44
48
|
}) => {
|
|
45
49
|
const { theme } = useContext(ApplicationContext);
|
|
46
50
|
const context = useContext<any>(MiniAppContext);
|
|
47
|
-
const
|
|
51
|
+
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
48
52
|
const gradientColor = customGradientColor ?? theme.colors.gradient;
|
|
49
53
|
const headerBackground = customBackground ?? theme.assets?.headerBackground;
|
|
50
54
|
const leftPosition = inputSearchProps?.leftPosition || BACK_WIDTH + 20;
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
let useShadowHeader = useShadowHeaderProp;
|
|
57
|
+
if (inputSearchProps && Platform.OS === 'android') {
|
|
58
|
+
useShadowHeader = false;
|
|
59
|
+
}
|
|
53
60
|
|
|
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
|
-
});
|
|
61
|
+
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
63
|
+
const heightStyle = useAnimatedStyle(() => ({
|
|
64
|
+
height: interpolate(
|
|
65
|
+
sharedValue.value,
|
|
66
|
+
[0, 100],
|
|
67
|
+
[heightHeader + 52, heightHeader],
|
|
68
|
+
Extrapolation.CLAMP,
|
|
69
|
+
),
|
|
70
|
+
}));
|
|
73
71
|
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
extrapolate: 'clamp',
|
|
78
|
-
});
|
|
72
|
+
const gradientOpacityStyle = useAnimatedStyle(() => ({
|
|
73
|
+
opacity: interpolate(sharedValue.value, [0, 52], [1, 0], Extrapolation.CLAMP),
|
|
74
|
+
}));
|
|
79
75
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
theme.colors.background.default,
|
|
76
|
+
const searchTranslateStyle = useAnimatedStyle(() => ({
|
|
77
|
+
transform: [
|
|
78
|
+
{
|
|
79
|
+
translateX: interpolate(
|
|
80
|
+
sharedValue.value,
|
|
81
|
+
[0, 100],
|
|
82
|
+
[SCREEN_PADDING, leftPosition],
|
|
83
|
+
Extrapolation.CLAMP,
|
|
84
|
+
),
|
|
85
|
+
},
|
|
91
86
|
],
|
|
92
|
-
|
|
87
|
+
width: interpolate(
|
|
88
|
+
sharedValue.value,
|
|
89
|
+
[0, 100],
|
|
90
|
+
[
|
|
91
|
+
screenWidth - SCREEN_PADDING * 2,
|
|
92
|
+
screenWidth - leftPosition - 12 - headerRightWidth,
|
|
93
|
+
],
|
|
94
|
+
Extrapolation.CLAMP,
|
|
95
|
+
),
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
const searchBackgroundStyle = useAnimatedStyle(() => {
|
|
99
|
+
const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
|
|
100
|
+
return { backgroundColor: t === 0
|
|
101
|
+
? theme.colors.background.surface
|
|
102
|
+
: theme.colors.background.default };
|
|
93
103
|
});
|
|
94
104
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
const extendedHeightStyle = useAnimatedStyle(() => ({
|
|
106
|
+
height: interpolate(
|
|
107
|
+
sharedValue.value,
|
|
108
|
+
[0, 100],
|
|
109
|
+
[heightHeader + 52, heightHeader],
|
|
110
|
+
Extrapolation.CLAMP,
|
|
111
|
+
),
|
|
112
|
+
}));
|
|
98
113
|
|
|
99
114
|
if (inputSearchProps) {
|
|
100
115
|
return (
|
|
101
116
|
<View style={[{ zIndex: 0 }, showBaseLineDebug && styles.debugBaseLine]}>
|
|
102
|
-
<Animated.View style={
|
|
117
|
+
<Animated.View style={heightStyle} />
|
|
103
118
|
<BackgroundImageView
|
|
104
119
|
useShadowHeader={useShadowHeader}
|
|
105
120
|
heightHeader={heightHeader}
|
|
106
|
-
|
|
121
|
+
animatedValue={sharedValue}
|
|
107
122
|
headerBackground={headerBackground}
|
|
108
123
|
/>
|
|
109
124
|
<Animated.View
|
|
110
125
|
style={[
|
|
111
126
|
styles.headerBox,
|
|
112
|
-
|
|
127
|
+
headerType === 'extended'
|
|
128
|
+
? heightStyle
|
|
129
|
+
: { height: heightHeader },
|
|
113
130
|
]}
|
|
114
131
|
>
|
|
115
132
|
{!!gradientColor && (
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
style={[styles.extendedHeader, { opacity: opacityGradient }]}
|
|
133
|
+
<Animated.View
|
|
134
|
+
style={[styles.extendedHeader, gradientOpacityStyle]}
|
|
119
135
|
>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
<LinearGradient
|
|
137
|
+
colors={[gradientColor, gradientColor + '00']}
|
|
138
|
+
style={StyleSheet.absoluteFill}
|
|
139
|
+
>
|
|
140
|
+
{!!theme.assets?.headerBackground && (
|
|
141
|
+
<Image
|
|
142
|
+
style={styles.headerBackground}
|
|
143
|
+
source={{ uri: theme.assets?.headerBackground }}
|
|
144
|
+
loading={false}
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
147
|
+
</LinearGradient>
|
|
148
|
+
</Animated.View>
|
|
128
149
|
)}
|
|
129
150
|
</Animated.View>
|
|
130
151
|
<Animated.View
|
|
131
|
-
style={
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
152
|
+
style={[
|
|
153
|
+
{
|
|
154
|
+
justifyContent: 'flex-end',
|
|
155
|
+
position: 'absolute',
|
|
156
|
+
zIndex: 2,
|
|
157
|
+
},
|
|
158
|
+
heightStyle,
|
|
159
|
+
]}
|
|
137
160
|
>
|
|
138
161
|
<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
|
-
}}
|
|
162
|
+
style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
|
|
151
163
|
>
|
|
152
|
-
<Animated.View
|
|
164
|
+
<Animated.View
|
|
165
|
+
style={[
|
|
166
|
+
{ borderRadius: Radius.XL },
|
|
167
|
+
searchBackgroundStyle,
|
|
168
|
+
]}
|
|
169
|
+
>
|
|
153
170
|
<InputSearch
|
|
154
171
|
{...inputSearchProps}
|
|
155
172
|
ref={inputSearchRef}
|
|
@@ -169,22 +186,26 @@ const HeaderExtendHeader: React.FC<{
|
|
|
169
186
|
<BackgroundImageView
|
|
170
187
|
useShadowHeader={useShadowHeader}
|
|
171
188
|
heightHeader={heightHeader}
|
|
172
|
-
|
|
189
|
+
animatedValue={sharedValue}
|
|
173
190
|
headerBackground={headerBackground}
|
|
174
191
|
/>
|
|
175
192
|
{!!gradientColor && (
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
style={[styles.extendedHeader, { opacity: opacityGradient }]}
|
|
193
|
+
<Animated.View
|
|
194
|
+
style={[styles.extendedHeader, gradientOpacityStyle, extendedHeightStyle]}
|
|
179
195
|
>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
196
|
+
<LinearGradient
|
|
197
|
+
colors={[gradientColor, gradientColor + '00']}
|
|
198
|
+
style={StyleSheet.absoluteFill}
|
|
199
|
+
>
|
|
200
|
+
{!!headerBackground && (
|
|
201
|
+
<Image
|
|
202
|
+
style={styles.headerBackground}
|
|
203
|
+
source={{ uri: headerBackground }}
|
|
204
|
+
loading={false}
|
|
205
|
+
/>
|
|
206
|
+
)}
|
|
207
|
+
</LinearGradient>
|
|
208
|
+
</Animated.View>
|
|
188
209
|
)}
|
|
189
210
|
<View style={{ height: heightHeader }} />
|
|
190
211
|
</View>
|
|
@@ -1,11 +1,15 @@
|
|
|
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
|
+
} from 'react-native-reanimated';
|
|
9
13
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
10
14
|
import { exportFontFamily, Text, useScaleSize } from '../../Text';
|
|
11
15
|
import { Colors, Radius, Spacing, Styles } from '../../Consts';
|
|
@@ -17,22 +21,51 @@ import {
|
|
|
17
21
|
import { Image } from '../../Image';
|
|
18
22
|
import { Icon } from '../../Icon';
|
|
19
23
|
import { Skeleton } from '../../Skeleton';
|
|
24
|
+
import {
|
|
25
|
+
AnimatedCompatValue,
|
|
26
|
+
useNormalizedSharedValue,
|
|
27
|
+
} from '../utils';
|
|
28
|
+
|
|
29
|
+
type HeaderTitleInterpolate = {
|
|
30
|
+
inputRange: number[];
|
|
31
|
+
outputRange: number[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type HeaderTitleExtraProps = {
|
|
35
|
+
animatedValue?: AnimatedCompatValue;
|
|
36
|
+
interpolate?: HeaderTitleInterpolate;
|
|
37
|
+
tintColor?: string;
|
|
38
|
+
children?: React.ReactNode;
|
|
39
|
+
};
|
|
20
40
|
|
|
21
41
|
/**
|
|
22
42
|
* default header title used for nav
|
|
23
43
|
*/
|
|
24
|
-
const HeaderTitle: React.FC<any> = props => {
|
|
44
|
+
const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = props => {
|
|
25
45
|
const context = useContext<any>(MiniAppContext);
|
|
26
46
|
|
|
27
47
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
28
48
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
const interpolateConfig = props.interpolate ?? {
|
|
50
|
+
inputRange: [0, 200],
|
|
51
|
+
outputRange: [0, 1],
|
|
52
|
+
};
|
|
53
|
+
const hasAnimated = props.animatedValue != null;
|
|
54
|
+
const sharedValue = useNormalizedSharedValue(props.animatedValue);
|
|
55
|
+
|
|
56
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
57
|
+
if (!hasAnimated) {
|
|
58
|
+
return { opacity: 1 };
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
opacity: interpolate(
|
|
62
|
+
sharedValue.value,
|
|
63
|
+
interpolateConfig.inputRange,
|
|
64
|
+
interpolateConfig.outputRange,
|
|
65
|
+
Extrapolation.CLAMP,
|
|
66
|
+
),
|
|
67
|
+
};
|
|
68
|
+
});
|
|
36
69
|
|
|
37
70
|
return (
|
|
38
71
|
<View
|
|
@@ -57,9 +90,9 @@ const HeaderTitle: React.FC<any> = props => {
|
|
|
57
90
|
},
|
|
58
91
|
{
|
|
59
92
|
fontFamily: exportFontFamily('bold', 'action_xs_bold'),
|
|
60
|
-
opacity,
|
|
61
93
|
color: props.tintColor,
|
|
62
94
|
},
|
|
95
|
+
animatedStyle,
|
|
63
96
|
]}
|
|
64
97
|
numberOfLines={1}
|
|
65
98
|
/>
|
|
@@ -1,16 +1,17 @@
|
|
|
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, { useAnimatedStyle } from 'react-native-reanimated';
|
|
10
|
+
import React, { useContext } from 'react';
|
|
11
11
|
import { SearchHeaderProps } from '../types';
|
|
12
12
|
import { ApplicationContext, MiniAppContext } from '../../Context';
|
|
13
13
|
import { Text } from '../../Text';
|
|
14
|
+
import { useNormalizedSharedValue } from '../utils';
|
|
14
15
|
|
|
15
16
|
const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
16
17
|
(
|
|
@@ -28,28 +29,17 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
28
29
|
const BACK_WIDTH = 28;
|
|
29
30
|
const { width: screenWidth } = Dimensions.get('window');
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
+
const sharedValue = useNormalizedSharedValue(animatedValue);
|
|
32
33
|
const leftPosition = props?.leftPosition ?? BACK_WIDTH + 20;
|
|
33
34
|
const headerRightWidth = props?.headerRightWidth ?? 73;
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
animatedValue?.removeListener(listener);
|
|
42
|
-
}
|
|
36
|
+
const backgroundStyle = useAnimatedStyle(() => {
|
|
37
|
+
const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
|
|
38
|
+
return {
|
|
39
|
+
backgroundColor: t === 0
|
|
40
|
+
? theme.colors.background.surface
|
|
41
|
+
: theme.colors.background.default,
|
|
43
42
|
};
|
|
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
43
|
});
|
|
54
44
|
|
|
55
45
|
const goBack = () => {
|
|
@@ -91,7 +81,9 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
|
|
|
91
81
|
},
|
|
92
82
|
]}
|
|
93
83
|
>
|
|
94
|
-
<Animated.View
|
|
84
|
+
<Animated.View
|
|
85
|
+
style={[{ borderRadius: Radius.XL }, backgroundStyle]}
|
|
86
|
+
>
|
|
95
87
|
<InputSearch
|
|
96
88
|
ref={ref}
|
|
97
89
|
{...props}
|
|
@@ -19,7 +19,7 @@ import Navigator from './Navigator';
|
|
|
19
19
|
import ScaleSizeProvider from './ScaleSizeProvider';
|
|
20
20
|
import { getModalOptions, getStackOptions } from './utils';
|
|
21
21
|
import { NavigationContainerProps } from './types';
|
|
22
|
-
import { ApplicationContext,
|
|
22
|
+
import { ApplicationContext, MiniAppContext } from '../Context';
|
|
23
23
|
import Localize from './Localize';
|
|
24
24
|
import { Colors, defaultTheme } from '../Consts';
|
|
25
25
|
import { HeaderLeft } from './Components/HeaderLeft';
|
|
@@ -29,9 +29,6 @@ import { HeaderBackground } from './Components/HeaderBackground';
|
|
|
29
29
|
|
|
30
30
|
const Stack = createStackNavigator();
|
|
31
31
|
|
|
32
|
-
// Observer storage key the host app writes FontScaleConfig to. Lowercased natively.
|
|
33
|
-
const FONT_SCALE_OBSERVER_KEY = 'font_scale_config';
|
|
34
|
-
|
|
35
32
|
const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
36
33
|
screen,
|
|
37
34
|
theme = defaultTheme,
|
|
@@ -48,11 +45,6 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
48
45
|
}) => {
|
|
49
46
|
const context = useContext<any>(MiniAppContext);
|
|
50
47
|
const [currentContext, setCurrentContext] = useState<any>({});
|
|
51
|
-
// Default = the host-seeded fontScaleConfig from MiniAppContext, so the first frame is already
|
|
52
|
-
// scaled (no flash). The observer below overwrites it on later host writes (live rate changes).
|
|
53
|
-
const [observerFontScaleConfig, setObserverFontScaleConfig] = useState<
|
|
54
|
-
FontScaleConfig | undefined
|
|
55
|
-
>(context?.fontScaleConfig);
|
|
56
48
|
|
|
57
49
|
const mergedContext = {
|
|
58
50
|
...context,
|
|
@@ -63,27 +55,10 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
63
55
|
},
|
|
64
56
|
};
|
|
65
57
|
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
// The initial value is seeded by the host into MiniAppContext and used as this state's default
|
|
68
|
-
// above, so the first frame is already scaled (no flash). Here we only subscribe to later host
|
|
69
|
-
// writes so a rate change applies live while the miniapp is open.
|
|
70
|
-
const sub = maxApi?.observer?.(
|
|
71
|
-
FONT_SCALE_OBSERVER_KEY,
|
|
72
|
-
(data: FontScaleConfig) => {
|
|
73
|
-
setObserverFontScaleConfig(data ?? undefined);
|
|
74
|
-
},
|
|
75
|
-
);
|
|
76
|
-
return () => {
|
|
77
|
-
sub?.remove?.();
|
|
78
|
-
};
|
|
79
|
-
}, [maxApi]);
|
|
80
|
-
|
|
81
58
|
return (
|
|
82
59
|
<SafeAreaProvider>
|
|
83
60
|
<MiniAppContext.Provider value={mergedContext}>
|
|
84
|
-
<ScaleSizeProvider
|
|
85
|
-
fontScaleConfig={observerFontScaleConfig}
|
|
86
|
-
>
|
|
61
|
+
<ScaleSizeProvider scaleSizeMaxRate={mergedContext?.scaleSizeMaxRate}>
|
|
87
62
|
<Navigation
|
|
88
63
|
screen={screen}
|
|
89
64
|
theme={theme}
|
|
@@ -142,6 +117,12 @@ const Navigation: React.FC<any> = ({
|
|
|
142
117
|
},
|
|
143
118
|
);
|
|
144
119
|
|
|
120
|
+
// TEMP: verify FontScale MaxApi end-to-end — remove after test
|
|
121
|
+
// @ts-ignore getFontScale added in newer @momo-platform/api
|
|
122
|
+
maxApi?.getFontScale?.((scale: any) =>
|
|
123
|
+
console.log('[FontScale][momo-kit] getFontScale ->', scale),
|
|
124
|
+
);
|
|
125
|
+
|
|
145
126
|
return () => {
|
|
146
127
|
subscription?.remove?.();
|
|
147
128
|
};
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import React, { FC
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
2
|
import { ScaleSizeContext } from '../Context';
|
|
3
3
|
import { ScaleSizeProviderProps } from './types';
|
|
4
4
|
|
|
5
5
|
const ScaleSizeProvider: FC<ScaleSizeProviderProps> = ({
|
|
6
|
-
|
|
6
|
+
scaleSizeMaxRate,
|
|
7
7
|
children,
|
|
8
8
|
}) => {
|
|
9
|
-
// Inherit the config from an upper layer (e.g. app root) when the host doesn't set it via MiniAppContext.
|
|
10
|
-
const parent = useContext(ScaleSizeContext);
|
|
11
9
|
return (
|
|
12
|
-
<ScaleSizeContext.Provider value={
|
|
10
|
+
<ScaleSizeContext.Provider value={{ scaleSizeMaxRate }}>
|
|
13
11
|
{children}
|
|
14
12
|
</ScaleSizeContext.Provider>
|
|
15
13
|
);
|
|
@@ -78,7 +78,7 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
|
|
|
78
78
|
<SafeAreaProvider>
|
|
79
79
|
<MiniAppContext.Provider value={mergedContext}>
|
|
80
80
|
<ScaleSizeProvider
|
|
81
|
-
|
|
81
|
+
scaleSizeMaxRate={mergedContext?.scaleSizeMaxRate}
|
|
82
82
|
>
|
|
83
83
|
<ApplicationContext.Provider
|
|
84
84
|
value={{
|
package/Application/types.ts
CHANGED
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
ViewProps,
|
|
8
8
|
ViewStyle,
|
|
9
9
|
} from 'react-native';
|
|
10
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
10
11
|
import { PopupNotifyProps } from '../Popup/types';
|
|
11
12
|
import { InputRef, InputSearchProps } from '../Input';
|
|
12
|
-
import { FontScaleConfig } from '../Context';
|
|
13
13
|
import Navigation from './Navigation';
|
|
14
14
|
|
|
15
15
|
export type NavigationProps = {
|
|
@@ -109,7 +109,7 @@ export type Theme = {
|
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
export type ScaleSizeProviderProps = {
|
|
112
|
-
|
|
112
|
+
scaleSizeMaxRate?: number;
|
|
113
113
|
children: ViewProps['children'];
|
|
114
114
|
};
|
|
115
115
|
|
|
@@ -259,7 +259,7 @@ export interface HeaderBackProps extends NavigationButtonProps {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
export type HeaderBackgroundProps = {
|
|
262
|
-
animatedValue?: Animated.Value
|
|
262
|
+
animatedValue?: Animated.Value | SharedValue<number>;
|
|
263
263
|
useGradient?: boolean;
|
|
264
264
|
useShadowHeader?: boolean;
|
|
265
265
|
backgroundColor?: string;
|
|
@@ -299,7 +299,7 @@ export type TitleJourneyProps = {
|
|
|
299
299
|
};
|
|
300
300
|
|
|
301
301
|
export interface HeaderAnimatedProps extends ViewProps {
|
|
302
|
-
animatedValue: Animated.Value
|
|
302
|
+
animatedValue: Animated.Value | SharedValue<number>;
|
|
303
303
|
image: string;
|
|
304
304
|
useScale?: boolean;
|
|
305
305
|
}
|
|
@@ -343,7 +343,7 @@ export type AnimatedHeader = {
|
|
|
343
343
|
|
|
344
344
|
export interface SearchHeaderProps extends InputSearchProps {
|
|
345
345
|
ref?: React.RefObject<InputRef>;
|
|
346
|
-
animatedValue?: Animated.Value
|
|
346
|
+
animatedValue?: Animated.Value | SharedValue<number>;
|
|
347
347
|
headerRightWidth?: 0 | 74 | 110 | number;
|
|
348
348
|
leftPosition?: 12 | 48 | number;
|
|
349
349
|
renderButtons?: () => ReactNode;
|
package/Application/utils.tsx
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
import type { HeaderTitleProps, NavigationOptions } from './types';
|
|
7
7
|
import { Colors, Spacing } from '../Consts';
|
|
8
8
|
import { Animated, AppState, Platform } from 'react-native';
|
|
9
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
10
|
+
import { useSharedValue } from 'react-native-reanimated';
|
|
9
11
|
import {
|
|
10
12
|
MiniAppContext,
|
|
11
13
|
ScreenContext,
|
|
@@ -60,7 +62,7 @@ const getModalOptions = (): StackNavigationOptions => {
|
|
|
60
62
|
*/
|
|
61
63
|
const getOptions = (
|
|
62
64
|
params: NavigationOptions,
|
|
63
|
-
animatedValue?:
|
|
65
|
+
animatedValue?: SharedValue<number>,
|
|
64
66
|
) => {
|
|
65
67
|
let options: StackNavigationOptions = {};
|
|
66
68
|
|
|
@@ -131,7 +133,7 @@ const getOptions = (
|
|
|
131
133
|
|
|
132
134
|
const exportHeaderTitle = (
|
|
133
135
|
params: NavigationOptions,
|
|
134
|
-
animatedValue?:
|
|
136
|
+
animatedValue?: SharedValue<number>,
|
|
135
137
|
): StackNavigationOptions => {
|
|
136
138
|
if (typeof params.headerTitle === 'object') {
|
|
137
139
|
return {
|
|
@@ -228,6 +230,43 @@ const useAppState = () => {
|
|
|
228
230
|
};
|
|
229
231
|
};
|
|
230
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Union of the two animation runtimes an internal foundation component might
|
|
235
|
+
* receive. Public Screen props still accept React Native Animated.Value only;
|
|
236
|
+
* this type keeps lower-level header/floating components compatible while they
|
|
237
|
+
* render with reanimated.
|
|
238
|
+
*/
|
|
239
|
+
export type AnimatedCompatValue = Animated.Value | SharedValue<number>;
|
|
240
|
+
|
|
241
|
+
const isRNAnimatedValue = (v: unknown): v is Animated.Value =>
|
|
242
|
+
!!v &&
|
|
243
|
+
typeof (v as Animated.Value).setValue === 'function' &&
|
|
244
|
+
typeof (v as Animated.Value).interpolate === 'function';
|
|
245
|
+
|
|
246
|
+
const isSharedValue = (v: unknown): v is SharedValue<number> =>
|
|
247
|
+
!!v && !isRNAnimatedValue(v) && 'value' in (v as object);
|
|
248
|
+
|
|
249
|
+
const useNormalizedSharedValue = (
|
|
250
|
+
input?: AnimatedCompatValue,
|
|
251
|
+
): SharedValue<number> => {
|
|
252
|
+
const fallback = useSharedValue(0);
|
|
253
|
+
|
|
254
|
+
useEffect(() => {
|
|
255
|
+
if (!isRNAnimatedValue(input)) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const id = input.addListener(({ value }) => {
|
|
259
|
+
fallback.value = value;
|
|
260
|
+
});
|
|
261
|
+
return () => input.removeListener(id);
|
|
262
|
+
}, [input, fallback]);
|
|
263
|
+
|
|
264
|
+
if (isSharedValue(input)) {
|
|
265
|
+
return input;
|
|
266
|
+
}
|
|
267
|
+
return fallback;
|
|
268
|
+
};
|
|
269
|
+
|
|
231
270
|
export {
|
|
232
271
|
getStackOptions,
|
|
233
272
|
getModalOptions,
|
|
@@ -236,4 +275,5 @@ export {
|
|
|
236
275
|
setAutomationID,
|
|
237
276
|
useComponentId,
|
|
238
277
|
useAppState,
|
|
278
|
+
useNormalizedSharedValue,
|
|
239
279
|
};
|