@momo-kits/foundation 0.81.1-optimize.21 → 0.81.1-rc.0
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 +27 -23
- package/Application/BottomTab/BottomTabBar.tsx +2 -2
- package/Application/BottomTab/index.tsx +2 -3
- package/Application/Components.tsx +288 -42
- package/Application/Localize.ts +44 -0
- package/Application/ModalScreen.tsx +2 -2
- package/Application/Navigation.ts +4 -18
- package/Application/NavigationContainer.tsx +10 -21
- package/Application/Navigator.ts +14 -16
- package/Application/StackScreen.tsx +211 -113
- package/Application/index.ts +5 -3
- package/Application/types.ts +30 -11
- package/Application/utils.tsx +60 -18
- package/Badge/BadgeDotAnimation.tsx +95 -0
- package/Badge/index.tsx +2 -1
- package/Badge/styles.ts +50 -6
- package/Button/index.tsx +0 -1
- package/Icon/types.ts +0 -1
- package/Input/Input.tsx +51 -17
- package/Input/InputDropDown.tsx +44 -47
- package/Input/InputMoney.tsx +56 -6
- package/Input/InputOTP.tsx +10 -6
- package/Input/InputSearch.tsx +259 -146
- package/Input/TextTyping.tsx +113 -0
- package/Input/common.tsx +29 -10
- package/Input/index.tsx +75 -74
- package/Input/styles.ts +43 -7
- package/Layout/FloatingButton.tsx +1 -1
- package/Layout/GridSystem.tsx +1 -1
- package/Layout/ItemList.tsx +3 -2
- package/Layout/Screen.tsx +174 -91
- package/Layout/styles.ts +0 -6
- package/Layout/types.ts +1 -1
- package/Pagination/PaginationScroll.tsx +2 -2
- package/Pagination/styles.ts +1 -1
- package/Popup/PopupNotify.tsx +3 -3
- package/Tag/index.tsx +9 -2
- package/Tag/styles.ts +3 -2
- package/Tag/types.ts +12 -0
- package/Title/index.tsx +6 -9
- package/Title/styles.ts +4 -4
- package/code-scanner.js +4 -4
- package/package.json +6 -5
- package/publish.sh +12 -22
- package/verify.js +36 -42
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {useRef, useEffect, useCallback, useContext} from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Dimensions,
|
|
5
5
|
KeyboardAvoidingView,
|
|
6
|
-
Modal,
|
|
7
6
|
PanResponder,
|
|
8
7
|
Platform,
|
|
9
8
|
StyleSheet,
|
|
10
9
|
TouchableOpacity,
|
|
11
10
|
View,
|
|
11
|
+
Modal,
|
|
12
12
|
} from 'react-native';
|
|
13
|
+
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
13
14
|
import {ApplicationContext} from './index';
|
|
14
15
|
import {BottomSheetParams} from './types';
|
|
15
|
-
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
|
16
16
|
import {Colors, Radius, Spacing, Styles} from '../Consts';
|
|
17
17
|
import {Text} from '../Text';
|
|
18
18
|
import {Icon} from '../Icon';
|
|
@@ -24,6 +24,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
24
24
|
const {
|
|
25
25
|
screen: Screen,
|
|
26
26
|
options,
|
|
27
|
+
useNativeModal = false,
|
|
27
28
|
surface,
|
|
28
29
|
barrierDismissible = false,
|
|
29
30
|
draggable = true,
|
|
@@ -36,6 +37,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
36
37
|
x: 0,
|
|
37
38
|
})
|
|
38
39
|
).current;
|
|
40
|
+
|
|
39
41
|
const panResponder = useRef(
|
|
40
42
|
PanResponder.create({
|
|
41
43
|
onStartShouldSetPanResponder: () => draggable,
|
|
@@ -61,7 +63,10 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
61
63
|
},
|
|
62
64
|
})
|
|
63
65
|
).current;
|
|
64
|
-
|
|
66
|
+
let Container: any = View;
|
|
67
|
+
if (useNativeModal) {
|
|
68
|
+
Container = Modal;
|
|
69
|
+
}
|
|
65
70
|
let backgroundColor = theme.colors.background.default;
|
|
66
71
|
if (surface) {
|
|
67
72
|
backgroundColor = theme.colors.background.surface;
|
|
@@ -88,20 +93,22 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
88
93
|
if (barrierDismissible && !force) {
|
|
89
94
|
return;
|
|
90
95
|
}
|
|
96
|
+
setTimeout(() => {
|
|
97
|
+
callback?.();
|
|
98
|
+
}, 300);
|
|
91
99
|
Animated.timing(pan, {
|
|
92
100
|
toValue: {x: 0, y: heightDevice},
|
|
93
101
|
useNativeDriver: false,
|
|
94
|
-
duration:
|
|
102
|
+
duration: 200,
|
|
95
103
|
}).start(() => {
|
|
96
104
|
navigator?.pop();
|
|
97
|
-
callback?.();
|
|
98
105
|
});
|
|
99
106
|
};
|
|
100
107
|
|
|
101
108
|
/**
|
|
102
|
-
*
|
|
109
|
+
* on request close
|
|
103
110
|
*/
|
|
104
|
-
const
|
|
111
|
+
const onRequestClose = useCallback((callback?: () => void) => {
|
|
105
112
|
onDismiss(true, callback);
|
|
106
113
|
}, []);
|
|
107
114
|
|
|
@@ -146,7 +153,11 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
146
153
|
}, []);
|
|
147
154
|
|
|
148
155
|
return (
|
|
149
|
-
<
|
|
156
|
+
<Container
|
|
157
|
+
transparent
|
|
158
|
+
visible={true}
|
|
159
|
+
onRequestClose={() => onDismiss()}
|
|
160
|
+
style={styles.overlay}>
|
|
150
161
|
<KeyboardAvoidingView
|
|
151
162
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
152
163
|
keyboardVerticalOffset={keyboardVerticalOffset ?? -20}
|
|
@@ -162,7 +173,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
162
173
|
<Screen
|
|
163
174
|
{...props}
|
|
164
175
|
{...props.route.params}
|
|
165
|
-
|
|
176
|
+
onRequestClose={onRequestClose}
|
|
166
177
|
/>
|
|
167
178
|
{useBottomInset && (
|
|
168
179
|
<View style={{height: Math.min(insets.bottom, 21)}} />
|
|
@@ -170,23 +181,14 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
170
181
|
</View>
|
|
171
182
|
</Animated.View>
|
|
172
183
|
</KeyboardAvoidingView>
|
|
173
|
-
</
|
|
184
|
+
</Container>
|
|
174
185
|
);
|
|
175
186
|
};
|
|
176
187
|
|
|
177
|
-
export default BottomSheet;
|
|
178
|
-
|
|
179
188
|
const styles = StyleSheet.create({
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
overflow: 'hidden',
|
|
184
|
-
borderTopLeftRadius: Radius.M,
|
|
185
|
-
borderTopRightRadius: Radius.M,
|
|
186
|
-
},
|
|
187
|
-
draggableContainer: {
|
|
188
|
-
width: '100%',
|
|
189
|
-
alignItems: 'center',
|
|
189
|
+
overlay: {
|
|
190
|
+
...StyleSheet.absoluteFillObject,
|
|
191
|
+
justifyContent: 'flex-end',
|
|
190
192
|
},
|
|
191
193
|
indicator: {
|
|
192
194
|
width: 40,
|
|
@@ -210,3 +212,5 @@ const styles = StyleSheet.create({
|
|
|
210
212
|
marginHorizontal: Spacing.M,
|
|
211
213
|
},
|
|
212
214
|
});
|
|
215
|
+
|
|
216
|
+
export default BottomSheet;
|
|
@@ -125,7 +125,7 @@ export default function BottomTabBar({
|
|
|
125
125
|
);
|
|
126
126
|
});
|
|
127
127
|
},
|
|
128
|
-
[JSON.stringify(routes), state, descriptors, focusedTab]
|
|
128
|
+
[JSON.stringify(routes), state, descriptors, focusedTab]
|
|
129
129
|
);
|
|
130
130
|
|
|
131
131
|
/**
|
|
@@ -153,7 +153,7 @@ export default function BottomTabBar({
|
|
|
153
153
|
</View>
|
|
154
154
|
<View
|
|
155
155
|
style={{
|
|
156
|
-
top: -
|
|
156
|
+
top: -4,
|
|
157
157
|
backgroundColor: activeTintColor ?? theme.colors.primary,
|
|
158
158
|
borderRadius: Radius.XL,
|
|
159
159
|
paddingHorizontal: 6,
|
|
@@ -17,10 +17,9 @@ const Tab = createBottomTabNavigator();
|
|
|
17
17
|
const Stack = createStackNavigator();
|
|
18
18
|
|
|
19
19
|
const TabScreen: React.FC<NavigationScreenProps> = ({route}) => {
|
|
20
|
-
const {theme} = useContext(ApplicationContext);
|
|
21
20
|
let options = {};
|
|
22
21
|
if (route.params?.options) {
|
|
23
|
-
options = getOptions(route.params?.options
|
|
22
|
+
options = getOptions(route.params?.options);
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
if (route.params?.nested) {
|
|
@@ -101,7 +100,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
|
|
|
101
100
|
name={item.name}
|
|
102
101
|
component={TabScreen}
|
|
103
102
|
initialParams={{...item, nested}}
|
|
104
|
-
listeners={listeners}
|
|
103
|
+
listeners={item.listeners ?? listeners}
|
|
105
104
|
options={{
|
|
106
105
|
tabBarLabel: ({color, focused}) => {
|
|
107
106
|
return (
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import React, {
|
|
2
|
-
|
|
2
|
+
Ref,
|
|
3
3
|
useCallback,
|
|
4
4
|
useContext,
|
|
5
5
|
useEffect,
|
|
6
|
+
useRef,
|
|
6
7
|
useState,
|
|
7
8
|
} from 'react';
|
|
8
9
|
import {
|
|
10
|
+
Alert,
|
|
9
11
|
Animated,
|
|
10
12
|
BackHandler,
|
|
11
13
|
DeviceEventEmitter,
|
|
14
|
+
Dimensions,
|
|
12
15
|
StatusBar,
|
|
13
16
|
StyleSheet,
|
|
14
17
|
TouchableOpacity,
|
|
@@ -29,7 +32,18 @@ import {scaleSize, Text} from '../Text';
|
|
|
29
32
|
import {Icon} from '../Icon';
|
|
30
33
|
import {PopupNotify} from '../Popup';
|
|
31
34
|
import {Badge, BadgeDot} from '../Badge';
|
|
35
|
+
import {HeaderType} from '../Layout/types';
|
|
36
|
+
import Navigation from './Navigation';
|
|
37
|
+
import {InputRef, InputSearch, InputSearchProps} from '../Input';
|
|
38
|
+
import {BadgeDotAnimation} from '../Badge';
|
|
32
39
|
|
|
40
|
+
const SCREEN_PADDING = 12;
|
|
41
|
+
const BACK_WIDTH = 28;
|
|
42
|
+
const {width: screenWidth} = Dimensions.get('window');
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* default navigation button used header nav
|
|
46
|
+
*/
|
|
33
47
|
const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
34
48
|
icon,
|
|
35
49
|
tintColor,
|
|
@@ -58,6 +72,11 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
|
58
72
|
if (badgeType === 'dot') {
|
|
59
73
|
return <BadgeDot size="small" style={styles.badgeDot} />;
|
|
60
74
|
}
|
|
75
|
+
if (badgeType === 'dot-animation') {
|
|
76
|
+
return (
|
|
77
|
+
<BadgeDotAnimation size="small" style={styles.badgeDotAnimation} />
|
|
78
|
+
);
|
|
79
|
+
}
|
|
61
80
|
|
|
62
81
|
if (isNumber(badgeValue)) {
|
|
63
82
|
return <Badge label={badgeValue} style={styles.badge} />;
|
|
@@ -65,7 +84,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
|
65
84
|
}, [badgeType, badgeValue]);
|
|
66
85
|
|
|
67
86
|
return (
|
|
68
|
-
<View style={{width:
|
|
87
|
+
<View style={{width: 28}}>
|
|
69
88
|
<TouchableOpacity
|
|
70
89
|
accessibilityLabel={accessibilityLabel}
|
|
71
90
|
style={[styles.navigationButton, buttonStyle]}
|
|
@@ -81,13 +100,18 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
|
|
|
81
100
|
);
|
|
82
101
|
};
|
|
83
102
|
|
|
103
|
+
/**
|
|
104
|
+
* default header title used for nav
|
|
105
|
+
*/
|
|
84
106
|
const HeaderTitle: React.FC<any> = props => {
|
|
85
107
|
const {theme} = useContext(ApplicationContext);
|
|
86
|
-
const opacity = props.animatedValue?.interpolate(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
108
|
+
const opacity = props.animatedValue?.interpolate(
|
|
109
|
+
props.interpolate ?? {
|
|
110
|
+
inputRange: [0, 200],
|
|
111
|
+
outputRange: [0, 1],
|
|
112
|
+
extrapolate: 'clamp',
|
|
113
|
+
}
|
|
114
|
+
);
|
|
91
115
|
|
|
92
116
|
return (
|
|
93
117
|
<Animated.Text
|
|
@@ -101,6 +125,9 @@ const HeaderTitle: React.FC<any> = props => {
|
|
|
101
125
|
);
|
|
102
126
|
};
|
|
103
127
|
|
|
128
|
+
/**
|
|
129
|
+
* default header left used for nav
|
|
130
|
+
*/
|
|
104
131
|
const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
105
132
|
tintColor,
|
|
106
133
|
preventBack,
|
|
@@ -113,7 +140,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
113
140
|
useEffect(() => {
|
|
114
141
|
const backHandler = BackHandler.addEventListener(
|
|
115
142
|
'hardwareBackPress',
|
|
116
|
-
goBackSafe
|
|
143
|
+
goBackSafe
|
|
117
144
|
);
|
|
118
145
|
|
|
119
146
|
return () => backHandler.remove();
|
|
@@ -168,6 +195,7 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
168
195
|
}
|
|
169
196
|
return true;
|
|
170
197
|
};
|
|
198
|
+
|
|
171
199
|
return (
|
|
172
200
|
<View style={styles.headerLeft}>
|
|
173
201
|
<NavigationButton
|
|
@@ -181,6 +209,9 @@ const HeaderLeft: React.FC<HeaderBackProps> = ({
|
|
|
181
209
|
);
|
|
182
210
|
};
|
|
183
211
|
|
|
212
|
+
/**
|
|
213
|
+
* header background for default
|
|
214
|
+
*/
|
|
184
215
|
const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
185
216
|
image,
|
|
186
217
|
animatedValue,
|
|
@@ -229,6 +260,10 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
|
|
|
229
260
|
);
|
|
230
261
|
};
|
|
231
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Header custom with image, title, subtitle
|
|
265
|
+
* @constructor
|
|
266
|
+
*/
|
|
232
267
|
const HeaderCustom: React.FC<TitleCustomProps> = ({
|
|
233
268
|
title,
|
|
234
269
|
subTitle,
|
|
@@ -263,37 +298,42 @@ const HeaderCustom: React.FC<TitleCustomProps> = ({
|
|
|
263
298
|
return <View style={styles.headerTitleContainer}>{content ?? header}</View>;
|
|
264
299
|
};
|
|
265
300
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
301
|
+
/**
|
|
302
|
+
* main component for header right
|
|
303
|
+
*/
|
|
304
|
+
const HeaderRight: React.FC<any> = ({type, children, onLayout, ...props}) => {
|
|
305
|
+
if (type === 'icon') {
|
|
306
|
+
let buttons = props?.buttons || [];
|
|
307
|
+
if (buttons?.length > 3) {
|
|
308
|
+
buttons = buttons.slice(0, 3);
|
|
273
309
|
}
|
|
274
|
-
return child;
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const renderAction = () => {
|
|
278
|
-
if (Array.isArray(children)) {
|
|
279
|
-
return children.map((child, index) => {
|
|
280
|
-
return (
|
|
281
|
-
<View key={`HeaderRightAction ${index}`} style={styles.headerButton}>
|
|
282
|
-
{React.cloneElement(validateType(child), {...restProps})}
|
|
283
|
-
</View>
|
|
284
|
-
);
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
310
|
return (
|
|
289
|
-
<View style={styles.
|
|
290
|
-
{
|
|
311
|
+
<View style={styles.headerRightButton} onLayout={onLayout}>
|
|
312
|
+
{buttons?.map?.((item: NavigationButtonProps, index: number) => {
|
|
313
|
+
return (
|
|
314
|
+
<View
|
|
315
|
+
key={`HeaderRightAction ${index}`}
|
|
316
|
+
style={{
|
|
317
|
+
marginLeft: index !== 0 ? Spacing.S : 0,
|
|
318
|
+
}}>
|
|
319
|
+
<NavigationButton {...item} tintColor={props?.tintColor} />
|
|
320
|
+
</View>
|
|
321
|
+
);
|
|
322
|
+
})}
|
|
291
323
|
</View>
|
|
292
324
|
);
|
|
293
|
-
}
|
|
294
|
-
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<View style={styles.headerRightButton} onLayout={onLayout}>
|
|
329
|
+
{children ?? <HeaderToolkitAction {...props} />}
|
|
330
|
+
</View>
|
|
331
|
+
);
|
|
295
332
|
};
|
|
296
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Header toolkit action
|
|
336
|
+
*/
|
|
297
337
|
const HeaderToolkitAction: React.FC<any> = ({
|
|
298
338
|
tintColor,
|
|
299
339
|
pinnedTool,
|
|
@@ -327,7 +367,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
327
367
|
(config: any) => {
|
|
328
368
|
navigator.toolkitConfig = config;
|
|
329
369
|
setToolConfig(navigator?.toolkitConfig);
|
|
330
|
-
}
|
|
370
|
+
}
|
|
331
371
|
);
|
|
332
372
|
};
|
|
333
373
|
|
|
@@ -338,9 +378,9 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
338
378
|
{runtimeTools},
|
|
339
379
|
(res: {item: {action?: string; key: string}}) => {
|
|
340
380
|
const {item} = res;
|
|
341
|
-
navigator?.toolkitCallback?.(item);
|
|
381
|
+
navigator?.toolkitCallback?.(item.key);
|
|
342
382
|
getToolkitConfig();
|
|
343
|
-
}
|
|
383
|
+
}
|
|
344
384
|
);
|
|
345
385
|
};
|
|
346
386
|
|
|
@@ -357,7 +397,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
357
397
|
navigator?.maxApi?.dispatchFunction?.(
|
|
358
398
|
'dismiss',
|
|
359
399
|
undefined,
|
|
360
|
-
undefined
|
|
400
|
+
undefined
|
|
361
401
|
);
|
|
362
402
|
},
|
|
363
403
|
}}
|
|
@@ -385,10 +425,10 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
385
425
|
onPress={() => {
|
|
386
426
|
navigator?.maxApi?.dispatchFunction?.(
|
|
387
427
|
'onToolAction',
|
|
388
|
-
|
|
428
|
+
{item: pinTool},
|
|
389
429
|
() => {
|
|
390
430
|
getToolkitConfig();
|
|
391
|
-
}
|
|
431
|
+
}
|
|
392
432
|
);
|
|
393
433
|
navigator?.toolkitCallback?.(pinTool.key);
|
|
394
434
|
}}
|
|
@@ -397,7 +437,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
397
437
|
};
|
|
398
438
|
if (toolConfig) {
|
|
399
439
|
return (
|
|
400
|
-
<View style={
|
|
440
|
+
<View style={Styles.row}>
|
|
401
441
|
{renderPinnedTool()}
|
|
402
442
|
<View
|
|
403
443
|
style={[
|
|
@@ -405,6 +445,7 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
405
445
|
{
|
|
406
446
|
backgroundColor: backgroundColor ?? '#00000066',
|
|
407
447
|
borderWidth: backgroundColor ? 0.5 : 0,
|
|
448
|
+
marginLeft: renderPinnedTool() ? Spacing.S : 0,
|
|
408
449
|
},
|
|
409
450
|
]}>
|
|
410
451
|
<TouchableOpacity
|
|
@@ -430,6 +471,9 @@ const HeaderToolkitAction: React.FC<any> = ({
|
|
|
430
471
|
return <View />;
|
|
431
472
|
};
|
|
432
473
|
|
|
474
|
+
/**
|
|
475
|
+
* default header banner for header animated
|
|
476
|
+
*/
|
|
433
477
|
const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
|
|
434
478
|
animatedValue,
|
|
435
479
|
image,
|
|
@@ -470,6 +514,191 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
|
|
|
470
514
|
);
|
|
471
515
|
};
|
|
472
516
|
|
|
517
|
+
/**
|
|
518
|
+
* Header extended with background image
|
|
519
|
+
* @constructor
|
|
520
|
+
*/
|
|
521
|
+
const HeaderExtendHeader: React.FC<{
|
|
522
|
+
headerType?: HeaderType;
|
|
523
|
+
animatedValue: Animated.Value;
|
|
524
|
+
heightHeader: number;
|
|
525
|
+
inputSearchProps?: InputSearchProps;
|
|
526
|
+
inputSearchRef?: Ref<InputRef>;
|
|
527
|
+
navigation?: Navigation;
|
|
528
|
+
}> = ({
|
|
529
|
+
headerType = 'default',
|
|
530
|
+
animatedValue,
|
|
531
|
+
heightHeader,
|
|
532
|
+
inputSearchProps,
|
|
533
|
+
inputSearchRef,
|
|
534
|
+
navigation,
|
|
535
|
+
}) => {
|
|
536
|
+
const {theme} = useContext(ApplicationContext);
|
|
537
|
+
const [rightSpace, setRightSpace] = useState(0);
|
|
538
|
+
const animated = useRef(new Animated.Value(0));
|
|
539
|
+
|
|
540
|
+
useEffect(() => {
|
|
541
|
+
const listener = animatedValue.addListener(({value}) => {
|
|
542
|
+
animated.current.setValue(value);
|
|
543
|
+
});
|
|
544
|
+
return () => {
|
|
545
|
+
animatedValue?.removeListener(listener);
|
|
546
|
+
};
|
|
547
|
+
}, []);
|
|
548
|
+
|
|
549
|
+
const height = animated.current.interpolate({
|
|
550
|
+
inputRange: [0, 100],
|
|
551
|
+
outputRange: [heightHeader + 52, heightHeader],
|
|
552
|
+
extrapolate: 'clamp',
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const translateX = animated.current.interpolate({
|
|
556
|
+
inputRange: [0, 100],
|
|
557
|
+
outputRange: [SCREEN_PADDING, BACK_WIDTH + SCREEN_PADDING * 2],
|
|
558
|
+
extrapolate: 'clamp',
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
if (navigation) {
|
|
562
|
+
navigation.onHeaderRightChange = (width: number) => {
|
|
563
|
+
setRightSpace(width);
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const renderInputView = (hasColorBG: boolean = false) => {
|
|
568
|
+
return (
|
|
569
|
+
<Animated.View
|
|
570
|
+
style={{
|
|
571
|
+
justifyContent: 'flex-end',
|
|
572
|
+
height,
|
|
573
|
+
}}>
|
|
574
|
+
<Animated.View
|
|
575
|
+
style={{
|
|
576
|
+
transform: [{translateX}],
|
|
577
|
+
marginVertical: Spacing.S,
|
|
578
|
+
width: animated.current.interpolate({
|
|
579
|
+
inputRange: [0, 100],
|
|
580
|
+
outputRange: [
|
|
581
|
+
screenWidth - SCREEN_PADDING * 2,
|
|
582
|
+
screenWidth - SCREEN_PADDING * 3 - BACK_WIDTH - rightSpace,
|
|
583
|
+
],
|
|
584
|
+
extrapolate: 'clamp',
|
|
585
|
+
}),
|
|
586
|
+
}}>
|
|
587
|
+
<InputSearch
|
|
588
|
+
{...inputSearchProps}
|
|
589
|
+
ref={inputSearchRef}
|
|
590
|
+
hasColorBG={hasColorBG}
|
|
591
|
+
showButtonText={false}
|
|
592
|
+
/>
|
|
593
|
+
</Animated.View>
|
|
594
|
+
</Animated.View>
|
|
595
|
+
);
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
if (inputSearchProps) {
|
|
599
|
+
if (headerType === 'surface') {
|
|
600
|
+
return (
|
|
601
|
+
<>
|
|
602
|
+
<Animated.View
|
|
603
|
+
style={[
|
|
604
|
+
styles.headerBox,
|
|
605
|
+
{
|
|
606
|
+
backgroundColor: theme.colors.background.surface,
|
|
607
|
+
borderBottomWidth: 1,
|
|
608
|
+
borderColor: theme.colors.border.default,
|
|
609
|
+
height,
|
|
610
|
+
},
|
|
611
|
+
]}
|
|
612
|
+
/>
|
|
613
|
+
{renderInputView(true)}
|
|
614
|
+
</>
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (headerType === 'extended') {
|
|
619
|
+
return (
|
|
620
|
+
<>
|
|
621
|
+
<Animated.View
|
|
622
|
+
style={[
|
|
623
|
+
styles.headerBox,
|
|
624
|
+
{
|
|
625
|
+
height,
|
|
626
|
+
},
|
|
627
|
+
]}>
|
|
628
|
+
<Image
|
|
629
|
+
source={{
|
|
630
|
+
uri: theme.assets?.headerBackground,
|
|
631
|
+
}}
|
|
632
|
+
style={styles.headerBackground}
|
|
633
|
+
/>
|
|
634
|
+
</Animated.View>
|
|
635
|
+
{renderInputView()}
|
|
636
|
+
</>
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return (
|
|
641
|
+
<>
|
|
642
|
+
<View style={[styles.headerBox, {height: heightHeader}]}>
|
|
643
|
+
<Image
|
|
644
|
+
source={{
|
|
645
|
+
uri: theme.assets?.headerBackground,
|
|
646
|
+
}}
|
|
647
|
+
style={styles.headerBackground}
|
|
648
|
+
/>
|
|
649
|
+
</View>
|
|
650
|
+
{renderInputView()}
|
|
651
|
+
</>
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (headerType === 'extended') {
|
|
656
|
+
return (
|
|
657
|
+
<View style={{minHeight: heightHeader}}>
|
|
658
|
+
<Image
|
|
659
|
+
source={{
|
|
660
|
+
uri: theme.assets?.headerBackground,
|
|
661
|
+
}}
|
|
662
|
+
style={styles.extendedHeader}
|
|
663
|
+
/>
|
|
664
|
+
</View>
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return <View />;
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
const HeaderRightAction: React.FC<any> = ({children, ...restProps}) => {
|
|
672
|
+
if (__DEV__) {
|
|
673
|
+
Alert.alert(
|
|
674
|
+
'HeaderRightAction',
|
|
675
|
+
'This component is deprecated and will removed 10/2024, please use new type headerRight specs instead v0.92.34'
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
const validateType = (child: React.ReactElement) => {
|
|
679
|
+
return child;
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
const renderAction = () => {
|
|
683
|
+
if (Array.isArray(children)) {
|
|
684
|
+
return children.map((child, index) => {
|
|
685
|
+
return (
|
|
686
|
+
<View key={`HeaderRightAction ${index}`} style={styles.headerButton}>
|
|
687
|
+
{React.cloneElement(validateType(child), {...restProps})}
|
|
688
|
+
</View>
|
|
689
|
+
);
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
return (
|
|
694
|
+
<View style={styles.headerButton}>
|
|
695
|
+
{React.cloneElement(validateType(children), {...restProps})}
|
|
696
|
+
</View>
|
|
697
|
+
);
|
|
698
|
+
};
|
|
699
|
+
return <View style={styles.headerRightButton}>{renderAction()}</View>;
|
|
700
|
+
};
|
|
701
|
+
|
|
473
702
|
const styles = StyleSheet.create({
|
|
474
703
|
navigationButton: {
|
|
475
704
|
height: 28,
|
|
@@ -509,7 +738,6 @@ const styles = StyleSheet.create({
|
|
|
509
738
|
paddingRight: Spacing.M,
|
|
510
739
|
},
|
|
511
740
|
toolkitContainer: {
|
|
512
|
-
marginLeft: Spacing.S,
|
|
513
741
|
padding: Spacing.XS,
|
|
514
742
|
height: 28,
|
|
515
743
|
borderRadius: 14,
|
|
@@ -541,6 +769,22 @@ const styles = StyleSheet.create({
|
|
|
541
769
|
top: -Spacing.XS,
|
|
542
770
|
right: -Spacing.XS,
|
|
543
771
|
},
|
|
772
|
+
badgeDotAnimation: {
|
|
773
|
+
position: 'absolute',
|
|
774
|
+
top: -Spacing.XS,
|
|
775
|
+
right: -Spacing.XS,
|
|
776
|
+
},
|
|
777
|
+
extendedHeader: {
|
|
778
|
+
aspectRatio: 1.75,
|
|
779
|
+
position: 'absolute',
|
|
780
|
+
width: '100%',
|
|
781
|
+
height: 210,
|
|
782
|
+
},
|
|
783
|
+
headerBox: {
|
|
784
|
+
width: '100%',
|
|
785
|
+
position: 'absolute',
|
|
786
|
+
overflow: 'hidden',
|
|
787
|
+
},
|
|
544
788
|
});
|
|
545
789
|
|
|
546
790
|
export {
|
|
@@ -548,8 +792,10 @@ export {
|
|
|
548
792
|
HeaderTitle,
|
|
549
793
|
HeaderLeft,
|
|
550
794
|
HeaderBackground,
|
|
551
|
-
HeaderRightAction,
|
|
552
795
|
HeaderToolkitAction,
|
|
553
796
|
HeaderCustom,
|
|
554
797
|
HeaderAnimated,
|
|
798
|
+
HeaderRight,
|
|
799
|
+
HeaderExtendHeader,
|
|
800
|
+
HeaderRightAction,
|
|
555
801
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {NativeModules} from 'react-native';
|
|
2
|
+
import {LocalizationObject} from './types';
|
|
3
|
+
import defaultLanguage from '../Assets/language.json';
|
|
4
|
+
|
|
5
|
+
class Localize {
|
|
6
|
+
private assets: LocalizationObject;
|
|
7
|
+
private currentLanguage: 'en' | 'vi';
|
|
8
|
+
|
|
9
|
+
constructor(assets: LocalizationObject) {
|
|
10
|
+
this.assets = {
|
|
11
|
+
vi: {...assets?.vi, ...defaultLanguage.vi},
|
|
12
|
+
en: {...assets?.en, ...defaultLanguage.en},
|
|
13
|
+
};
|
|
14
|
+
this.currentLanguage = 'vi';
|
|
15
|
+
if (NativeModules?.RNResource?.language === 'en') {
|
|
16
|
+
this.currentLanguage = 'en';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get getCurrentLanguage() {
|
|
21
|
+
return this.currentLanguage;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
translate(key: string) {
|
|
25
|
+
return this.assets[this.currentLanguage]?.[key] || key;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
translateData(data: {vi: string; en: string}) {
|
|
29
|
+
return data[this.currentLanguage] || data.vi || JSON.stringify(data);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
addTranslations(translations: LocalizationObject) {
|
|
33
|
+
this.assets = {
|
|
34
|
+
vi: {...translations?.vi, ...this.assets.vi},
|
|
35
|
+
en: {...translations?.en, ...this.assets.en},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setLanguage(language: 'en' | 'vi') {
|
|
40
|
+
this.currentLanguage = language;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default Localize;
|