@momo-kits/foundation 0.77.8-beta.4 → 0.77.8-beta.6
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/ModalScreen.tsx +6 -33
- package/Application/NavigationContainer.tsx +22 -1
- package/Application/types.ts +15 -16
- package/Button/index.tsx +20 -0
- package/Divider/index.tsx +2 -0
- package/Input/Input.tsx +126 -107
- package/Input/InputMoney.tsx +118 -111
- package/Input/InputSearch.tsx +129 -124
- package/Input/InputTextArea.tsx +126 -119
- package/Input/index.tsx +5 -0
- package/Layout/Card.tsx +9 -2
- package/Layout/Item.tsx +15 -2
- package/Layout/Screen.tsx +61 -3
- package/Layout/Section.tsx +36 -3
- package/Layout/types.ts +0 -37
- package/Loader/DotLoader.tsx +10 -12
- package/Loader/Loader.tsx +3 -4
- package/Loader/Spinner.tsx +8 -10
- package/Loader/types.ts +24 -0
- package/Popup/PopupNotify.tsx +2 -10
- package/Skeleton/types.ts +3 -0
- package/Text/utils.ts +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
useCallback,
|
|
3
|
-
useContext,
|
|
4
|
-
useEffect,
|
|
5
|
-
useMemo,
|
|
6
|
-
useRef,
|
|
7
|
-
useState,
|
|
8
|
-
} from 'react';
|
|
1
|
+
import React, {useContext, useEffect, useRef} from 'react';
|
|
9
2
|
import {
|
|
3
|
+
Dimensions,
|
|
10
4
|
KeyboardAvoidingView,
|
|
11
5
|
Platform,
|
|
12
6
|
Pressable,
|
|
@@ -91,10 +85,11 @@ const Modal: React.FC<ModalParams> = props => {
|
|
|
91
85
|
|
|
92
86
|
const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
93
87
|
const {theme, navigator} = useContext(ApplicationContext);
|
|
88
|
+
const {height} = Dimensions.get('window');
|
|
94
89
|
const {
|
|
95
90
|
screen,
|
|
96
91
|
options,
|
|
97
|
-
|
|
92
|
+
snapPoints = [height * 0.5, height * 0.9],
|
|
98
93
|
}: BottomSheetParams = props.route.params;
|
|
99
94
|
const Component = useRef(screen).current;
|
|
100
95
|
const navigation = new Navigation(props.navigation, theme);
|
|
@@ -105,31 +100,9 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
105
100
|
|
|
106
101
|
delete params.screen;
|
|
107
102
|
|
|
108
|
-
const [contentHeight, setContentHeight] = useState(undefined);
|
|
109
|
-
const snapPoints = useMemo(() => {
|
|
110
|
-
if (useSnap) {
|
|
111
|
-
return ['50%', '90%'];
|
|
112
|
-
}
|
|
113
|
-
return [contentHeight ?? '50%'];
|
|
114
|
-
}, [contentHeight]);
|
|
115
|
-
|
|
116
103
|
const bottomSheetRef = useRef<any>(null);
|
|
117
104
|
const mountedRef = useRef(true);
|
|
118
105
|
|
|
119
|
-
let handleOnLayout;
|
|
120
|
-
if (useSnap) {
|
|
121
|
-
handleOnLayout = useCallback(
|
|
122
|
-
({
|
|
123
|
-
nativeEvent: {
|
|
124
|
-
layout: {height},
|
|
125
|
-
},
|
|
126
|
-
}) => {
|
|
127
|
-
setContentHeight(height);
|
|
128
|
-
},
|
|
129
|
-
[],
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
106
|
let backgroundColor = theme.colors.background.default;
|
|
134
107
|
if (options?.surface) {
|
|
135
108
|
backgroundColor = theme.colors.background.surface;
|
|
@@ -154,7 +127,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
154
127
|
<SafeAreaView style={Styles.flex}>
|
|
155
128
|
<BottomSheetModal
|
|
156
129
|
ref={bottomSheetRef}
|
|
157
|
-
snapPoints={snapPoints}
|
|
130
|
+
snapPoints={snapPoints?.map(item => item + 68)}
|
|
158
131
|
onDismiss={() => {
|
|
159
132
|
if (mountedRef.current) {
|
|
160
133
|
navigator?.pop();
|
|
@@ -162,7 +135,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
|
|
|
162
135
|
}}
|
|
163
136
|
handleComponent={null}
|
|
164
137
|
backdropComponent={backdropComponent}>
|
|
165
|
-
<View
|
|
138
|
+
<View accessibilityLabel={'BottomSheet'}>
|
|
166
139
|
<View style={[styles.sheetContainer, {backgroundColor}]}>
|
|
167
140
|
<View style={styles.indicatorContainer}>
|
|
168
141
|
<View
|
|
@@ -21,10 +21,12 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
21
21
|
theme,
|
|
22
22
|
options,
|
|
23
23
|
maxApi,
|
|
24
|
+
localization,
|
|
24
25
|
}) => {
|
|
25
26
|
const navigationRef = React.useRef<NavigationContainerRef>(null);
|
|
26
27
|
const navigator = useRef(new Navigator(navigationRef));
|
|
27
28
|
const [showGrid, setShowGrid] = useState(false);
|
|
29
|
+
const [language, setLanguage] = useState<'vi' | 'en'>('vi');
|
|
28
30
|
|
|
29
31
|
useEffect(() => {
|
|
30
32
|
const subscription = DeviceEventEmitter.addListener(
|
|
@@ -38,13 +40,32 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
|
|
|
38
40
|
};
|
|
39
41
|
}, []);
|
|
40
42
|
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const listener = maxApi?.observer?.('language', (lang: 'vi' | 'en') => {
|
|
45
|
+
setLanguage(lang);
|
|
46
|
+
});
|
|
47
|
+
return () => {
|
|
48
|
+
listener?.();
|
|
49
|
+
};
|
|
50
|
+
}, []);
|
|
51
|
+
|
|
41
52
|
navigator.current.maxApi = maxApi;
|
|
42
53
|
|
|
54
|
+
const translate = (key: string) => {
|
|
55
|
+
return localization?.[language]?.[key] || '';
|
|
56
|
+
};
|
|
57
|
+
|
|
43
58
|
return (
|
|
44
59
|
<SafeAreaProvider>
|
|
45
60
|
<BottomSheetModalProvider>
|
|
46
61
|
<ApplicationContext.Provider
|
|
47
|
-
value={{
|
|
62
|
+
value={{
|
|
63
|
+
theme,
|
|
64
|
+
navigator: navigator.current,
|
|
65
|
+
showGrid,
|
|
66
|
+
language,
|
|
67
|
+
translate,
|
|
68
|
+
}}>
|
|
48
69
|
<ReactNavigationContainer
|
|
49
70
|
theme={{
|
|
50
71
|
...theme,
|
package/Application/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {Animated
|
|
2
|
-
import React
|
|
1
|
+
import {Animated} from 'react-native';
|
|
2
|
+
import React from 'react';
|
|
3
3
|
import Navigator from './Navigator';
|
|
4
4
|
import {PopupNotifyProps} from '../Popup/types';
|
|
5
5
|
import Navigation from './Navigation';
|
|
@@ -65,6 +65,17 @@ export type Context = {
|
|
|
65
65
|
theme: Theme;
|
|
66
66
|
navigator?: Navigator;
|
|
67
67
|
showGrid?: boolean;
|
|
68
|
+
language: 'vi' | 'en';
|
|
69
|
+
translate: (key: string) => string;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export type LocalizationObject = {
|
|
73
|
+
vi: {
|
|
74
|
+
[key: string]: string;
|
|
75
|
+
};
|
|
76
|
+
en: {
|
|
77
|
+
[key: string]: string;
|
|
78
|
+
};
|
|
68
79
|
};
|
|
69
80
|
|
|
70
81
|
export type NavigationContainerProps = {
|
|
@@ -72,6 +83,7 @@ export type NavigationContainerProps = {
|
|
|
72
83
|
options?: NavigationOptions;
|
|
73
84
|
theme: Theme;
|
|
74
85
|
maxApi?: any;
|
|
86
|
+
localization: LocalizationObject;
|
|
75
87
|
};
|
|
76
88
|
|
|
77
89
|
export type NavigationScreenProps = {
|
|
@@ -80,19 +92,6 @@ export type NavigationScreenProps = {
|
|
|
80
92
|
navigation: Navigation;
|
|
81
93
|
};
|
|
82
94
|
|
|
83
|
-
export interface ScreenProps extends ViewProps {
|
|
84
|
-
navigation?: Navigation;
|
|
85
|
-
enableKeyboardAvoidingView?: boolean;
|
|
86
|
-
headerBackground?: string;
|
|
87
|
-
scrollable?: boolean;
|
|
88
|
-
useAnimatedHeader?: boolean;
|
|
89
|
-
useExtendedHeader?: boolean;
|
|
90
|
-
scrollViewProps?: ScrollViewProps;
|
|
91
|
-
footerComponent?: ReactNode;
|
|
92
|
-
marginHeader?: number;
|
|
93
|
-
backgroundColor?: string;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
95
|
export type ScreenParams = {
|
|
97
96
|
[key: string]: any;
|
|
98
97
|
screen: Screen;
|
|
@@ -108,7 +107,7 @@ export interface ModalParams extends ScreenParams {
|
|
|
108
107
|
export interface BottomSheetParams extends ScreenParams {
|
|
109
108
|
[key: string]: any;
|
|
110
109
|
onDismiss?: () => void;
|
|
111
|
-
|
|
110
|
+
snapPoints?: ReadonlyArray<number>;
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
export type NavigationButtonProps = {
|
package/Button/index.tsx
CHANGED
|
@@ -11,6 +11,7 @@ import {Typography} from '../Text/types';
|
|
|
11
11
|
import {Colors} from '../Consts';
|
|
12
12
|
import styles from './styles';
|
|
13
13
|
import {Icon} from '../Icon';
|
|
14
|
+
import {Loader} from '../Loader';
|
|
14
15
|
|
|
15
16
|
export interface ButtonProps extends TouchableOpacityProps {
|
|
16
17
|
/**
|
|
@@ -55,6 +56,11 @@ export interface ButtonProps extends TouchableOpacityProps {
|
|
|
55
56
|
*/
|
|
56
57
|
useTintColor?: boolean;
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* If true, the icon of button will use be show indicator loading.
|
|
61
|
+
*/
|
|
62
|
+
loading?: boolean;
|
|
63
|
+
|
|
58
64
|
/**
|
|
59
65
|
* A callback function that is called when the button is pressed.
|
|
60
66
|
*/
|
|
@@ -68,6 +74,7 @@ const Button: FC<ButtonProps> = ({
|
|
|
68
74
|
full = true,
|
|
69
75
|
iconRight,
|
|
70
76
|
iconLeft,
|
|
77
|
+
loading = false,
|
|
71
78
|
title = 'Button',
|
|
72
79
|
onPress,
|
|
73
80
|
...rest
|
|
@@ -223,6 +230,19 @@ const Button: FC<ButtonProps> = ({
|
|
|
223
230
|
const renderTrailing = () => {
|
|
224
231
|
const marginLeft = getIconSpace();
|
|
225
232
|
const color = useTintColor ? getTextColor() : undefined;
|
|
233
|
+
if (loading) {
|
|
234
|
+
const iconSize = getIconSize();
|
|
235
|
+
const color = getTextColor();
|
|
236
|
+
return (
|
|
237
|
+
<View
|
|
238
|
+
style={[
|
|
239
|
+
styles.trailing,
|
|
240
|
+
{width: iconSize, height: iconSize, marginLeft},
|
|
241
|
+
]}>
|
|
242
|
+
<Loader type="spinner" color={color} />
|
|
243
|
+
</View>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
226
246
|
if (iconRight) {
|
|
227
247
|
const iconSize = getIconSize();
|
|
228
248
|
return (
|
package/Divider/index.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, {useContext} from 'react';
|
|
2
2
|
import {View} from 'react-native';
|
|
3
3
|
import {ApplicationContext} from '../Application';
|
|
4
|
+
import {Spacing} from '../Consts';
|
|
4
5
|
|
|
5
6
|
const Divider = () => {
|
|
6
7
|
const {theme} = useContext(ApplicationContext);
|
|
@@ -11,6 +12,7 @@ const Divider = () => {
|
|
|
11
12
|
height: 1,
|
|
12
13
|
width: '100%',
|
|
13
14
|
backgroundColor: theme.colors.border.default,
|
|
15
|
+
marginVertical: Spacing.XS,
|
|
14
16
|
}}
|
|
15
17
|
/>
|
|
16
18
|
);
|
package/Input/Input.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {FC, useContext, useRef, useState} from 'react';
|
|
1
|
+
import React, {FC, forwardRef, useContext, useRef, useState} from 'react';
|
|
2
2
|
import {
|
|
3
3
|
NativeSyntheticEvent,
|
|
4
4
|
TextInput,
|
|
@@ -12,125 +12,144 @@ import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
|
|
|
12
12
|
import {InputProps} from './index';
|
|
13
13
|
import {Icon} from '../Icon';
|
|
14
14
|
import {checkTyping} from './utils';
|
|
15
|
+
import {Loader} from '../Loader';
|
|
15
16
|
|
|
16
|
-
const Input: FC<InputProps> = (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
17
|
+
const Input: FC<InputProps> = forwardRef(
|
|
18
|
+
(
|
|
19
|
+
{
|
|
20
|
+
value,
|
|
21
|
+
onChangeText,
|
|
22
|
+
floatingValue,
|
|
23
|
+
floatingIcon,
|
|
24
|
+
size,
|
|
25
|
+
placeholder,
|
|
26
|
+
onBlur,
|
|
27
|
+
onFocus,
|
|
28
|
+
errorMessage,
|
|
29
|
+
icon,
|
|
30
|
+
disabled,
|
|
31
|
+
floatingIconColor,
|
|
32
|
+
iconColor,
|
|
33
|
+
required,
|
|
34
|
+
errorSpacing,
|
|
35
|
+
loading,
|
|
36
|
+
...props
|
|
37
|
+
},
|
|
38
|
+
ref,
|
|
39
|
+
) => {
|
|
40
|
+
const {theme} = useContext(ApplicationContext);
|
|
41
|
+
const [focused, setFocused] = useState(false);
|
|
42
|
+
const [haveValue, setHaveValue] = useState(!!value || !!props.defaultValue);
|
|
43
|
+
const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
const onClearText = () => {
|
|
46
|
+
inputRef?.current?.clear();
|
|
47
|
+
_onChangeText('');
|
|
48
|
+
};
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
const _onChangeText = (text: string) => {
|
|
51
|
+
checkTyping(text, haveValue, setHaveValue);
|
|
52
|
+
onChangeText?.(text);
|
|
53
|
+
};
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
56
|
+
setFocused(true);
|
|
57
|
+
onFocus?.(e);
|
|
58
|
+
};
|
|
49
59
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
61
|
+
setFocused(false);
|
|
62
|
+
onBlur?.(e);
|
|
63
|
+
};
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
const renderIcon = (color: string | undefined) => {
|
|
66
|
+
if (loading) {
|
|
67
|
+
return <Loader type={'spinner'} color={color} />;
|
|
68
|
+
}
|
|
69
|
+
if (icon) {
|
|
70
|
+
return <Icon color={color} source={icon} size={24} />;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
const renderInputView = () => {
|
|
75
|
+
const disabledColor = theme.colors.text.disable;
|
|
76
|
+
let textColor = theme.colors.text.default;
|
|
77
|
+
let placeholderColor = theme.colors.text.hint;
|
|
78
|
+
let iconTintColor = iconColor;
|
|
65
79
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
80
|
+
if (disabled) {
|
|
81
|
+
textColor = disabledColor;
|
|
82
|
+
placeholderColor = disabledColor;
|
|
83
|
+
iconTintColor = disabledColor;
|
|
84
|
+
}
|
|
71
85
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
/>
|
|
87
|
-
<View style={styles.inputView}>
|
|
88
|
-
<TextInput
|
|
89
|
-
{...props}
|
|
90
|
-
accessibilityLabel={`TextInput`}
|
|
91
|
-
editable={!disabled}
|
|
92
|
-
textAlignVertical="top"
|
|
93
|
-
ref={inputRef}
|
|
94
|
-
style={[
|
|
95
|
-
styles.input,
|
|
96
|
-
{
|
|
97
|
-
color: textColor,
|
|
98
|
-
fontFamily: `${theme.font}-Regular`,
|
|
99
|
-
},
|
|
100
|
-
]}
|
|
101
|
-
textBreakStrategy="highQuality"
|
|
102
|
-
value={value}
|
|
103
|
-
onChangeText={_onChangeText}
|
|
104
|
-
onFocus={_onFocus}
|
|
105
|
-
onBlur={_onBlur}
|
|
106
|
-
placeholder={placeholder}
|
|
107
|
-
selectionColor={theme.colors.primary}
|
|
108
|
-
placeholderTextColor={placeholderColor}
|
|
86
|
+
return (
|
|
87
|
+
<View
|
|
88
|
+
style={[
|
|
89
|
+
styles.inputWrapper,
|
|
90
|
+
{backgroundColor: theme.colors.background.surface},
|
|
91
|
+
getSizeStyle(size),
|
|
92
|
+
getBorderColor(theme, focused, errorMessage, disabled),
|
|
93
|
+
]}>
|
|
94
|
+
<FloatingView
|
|
95
|
+
floatingValue={floatingValue}
|
|
96
|
+
floatingIconColor={floatingIconColor}
|
|
97
|
+
disabled={disabled}
|
|
98
|
+
required={required}
|
|
99
|
+
floatingIcon={floatingIcon}
|
|
109
100
|
/>
|
|
101
|
+
<View style={styles.inputView}>
|
|
102
|
+
<TextInput
|
|
103
|
+
{...props}
|
|
104
|
+
accessibilityLabel={`TextInput`}
|
|
105
|
+
editable={!disabled}
|
|
106
|
+
textAlignVertical="top"
|
|
107
|
+
ref={inputRef}
|
|
108
|
+
style={[
|
|
109
|
+
styles.input,
|
|
110
|
+
{
|
|
111
|
+
color: textColor,
|
|
112
|
+
fontFamily: `${theme.font}-Regular`,
|
|
113
|
+
},
|
|
114
|
+
]}
|
|
115
|
+
textBreakStrategy="highQuality"
|
|
116
|
+
value={value}
|
|
117
|
+
onChangeText={_onChangeText}
|
|
118
|
+
onFocus={_onFocus}
|
|
119
|
+
onBlur={_onBlur}
|
|
120
|
+
placeholder={placeholder}
|
|
121
|
+
selectionColor={theme.colors.primary}
|
|
122
|
+
placeholderTextColor={placeholderColor}
|
|
123
|
+
/>
|
|
124
|
+
</View>
|
|
125
|
+
<View style={styles.iconView}>
|
|
126
|
+
{focused && haveValue && (
|
|
127
|
+
<TouchableOpacity
|
|
128
|
+
style={styles.iconWrapper}
|
|
129
|
+
onPress={onClearText}>
|
|
130
|
+
<Icon
|
|
131
|
+
source="24_navigation_close_circle_full"
|
|
132
|
+
size={16}
|
|
133
|
+
color={theme.colors.text.hint}
|
|
134
|
+
/>
|
|
135
|
+
</TouchableOpacity>
|
|
136
|
+
)}
|
|
137
|
+
{renderIcon(iconTintColor)}
|
|
138
|
+
</View>
|
|
110
139
|
</View>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)}
|
|
121
|
-
{!!icon && <Icon color={iconTintColor} source={icon} size={24} />}
|
|
122
|
-
</View>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<View
|
|
145
|
+
style={styles.wrapper}
|
|
146
|
+
accessibilityLabel={`Input/${floatingValue}`}>
|
|
147
|
+
{renderInputView()}
|
|
148
|
+
<ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
|
|
123
149
|
</View>
|
|
124
150
|
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<View style={styles.wrapper} accessibilityLabel={`Input/${floatingValue}`}>
|
|
129
|
-
{renderInputView()}
|
|
130
|
-
<ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
|
|
131
|
-
</View>
|
|
132
|
-
);
|
|
133
|
-
};
|
|
151
|
+
},
|
|
152
|
+
);
|
|
134
153
|
|
|
135
154
|
Input.defaultProps = {
|
|
136
155
|
size: 'small',
|