@momo-kits/foundation 0.169.9-beta.1 → 0.169.9
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.hooks.ts +27 -17
- package/Application/BottomSheet.tsx +35 -27
- package/Application/BottomTab/ActiveIndicator.tsx +5 -1
- package/Application/BottomTab/BottomTabBar.styles.ts +18 -1
- package/Application/BottomTab/BottomTabBar.tsx +14 -16
- package/Application/BottomTab/CurvedContainer.tsx +8 -2
- package/Application/BottomTab/CustomBottomTabItem.tsx +2 -2
- package/Application/BottomTab/DuoRailTabBar.tsx +5 -5
- package/Application/BottomTab/DuoTabBar.tsx +15 -3
- package/Application/BottomTab/TabScreen.tsx +11 -10
- package/Application/BottomTab/index.tsx +5 -5
- package/Application/BottomTab/metrics.ts +23 -3
- package/Application/BottomTab/usePressLift.ts +3 -3
- package/Application/Components/HeaderAnimated.tsx +13 -38
- package/Application/Components/HeaderBackground.tsx +3 -16
- package/Application/Components/HeaderExtendHeader.tsx +28 -41
- package/Application/Components/HeaderLeft.tsx +18 -4
- package/Application/Components/HeaderRight.styles.ts +13 -15
- package/Application/Components/HeaderTitle.shared.ts +11 -10
- package/Application/Components/HeaderToolkitAction.tsx +54 -21
- package/Application/Components/NavigationButton.tsx +6 -9
- package/Application/Components/SearchHeader.tsx +7 -20
- package/Application/Components/useToolkitActions.tsx +22 -21
- package/Application/ModalScreen.tsx +19 -15
- package/Application/StackScreen.tsx +13 -18
- package/Application/index.ts +1 -11
- package/Application/modalLayout.ts +1 -0
- package/Application/navigationOptions.tsx +15 -6
- package/Application/types.header.ts +2 -7
- package/Application/types.tabs.ts +0 -1
- package/Application/types.ts +0 -7
- package/Context/index.ts +3 -1
- package/Image/index.tsx +6 -6
- package/Input/ErrorView.tsx +59 -0
- package/Input/FloatingView.tsx +101 -0
- package/Input/Input.tsx +40 -37
- package/Input/InputDropDown.tsx +13 -10
- package/Input/InputMoney.tsx +17 -17
- package/Input/InputMoneyView.tsx +12 -12
- package/Input/InputOTP.tsx +46 -34
- package/Input/InputPhoneNumber.tsx +48 -43
- package/Input/InputSearch.tsx +26 -39
- package/Input/InputTextArea.tsx +29 -23
- package/Input/RenderTrailing.tsx +79 -0
- package/Input/constants.ts +8 -0
- package/Input/index.tsx +1 -7
- package/Input/utils.ts +126 -7
- package/Layout/FloatingButton.tsx +38 -24
- package/Layout/Screen.tsx +0 -3
- package/Layout/layoutMetrics.ts +43 -34
- package/Layout/screenHeader.tsx +52 -55
- package/Layout/screenTypes.ts +4 -13
- package/Pagination/PaginationScroll.tsx +5 -3
- package/Pagination/types.ts +3 -5
- package/Popup/PopupNotify.tsx +17 -14
- package/Popup/PopupPromotion.tsx +5 -4
- package/Popup/types.ts +2 -4
- package/Popup/utils.ts +7 -4
- package/Skeleton/index.tsx +15 -12
- package/package.json +1 -1
- package/Application/headerLayout.ts +0 -165
- package/Input/common.tsx +0 -257
- package/Input/moneyUtils.ts +0 -23
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { type FC, useContext } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
type GestureResponderEvent,
|
|
4
|
+
TouchableOpacity,
|
|
5
|
+
View,
|
|
6
|
+
type ViewStyle,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import { ApplicationContext } from '../Context';
|
|
9
|
+
import { Icon } from '../Icon';
|
|
10
|
+
import { Text, useScaleSize } from '../Text';
|
|
11
|
+
import { PRESSED_OPACITY } from './constants';
|
|
12
|
+
import styles from './styles';
|
|
13
|
+
|
|
14
|
+
const LABEL_OFFSET = 10;
|
|
15
|
+
const ICON_SIZE = 16;
|
|
16
|
+
|
|
17
|
+
type FloatingViewProps = {
|
|
18
|
+
floatingValue?: string;
|
|
19
|
+
floatingIconColor?: string;
|
|
20
|
+
componentId?: string;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
floatingIcon?: string;
|
|
23
|
+
required?: boolean;
|
|
24
|
+
style?: ViewStyle;
|
|
25
|
+
onPress?: (e: GestureResponderEvent) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Floating label on the input's top border with a required `*` and an
|
|
30
|
+
* optional pressable icon; renders nothing without `floatingValue`.
|
|
31
|
+
*/
|
|
32
|
+
const FloatingView: FC<FloatingViewProps> = ({
|
|
33
|
+
floatingValue,
|
|
34
|
+
floatingIconColor,
|
|
35
|
+
disabled,
|
|
36
|
+
floatingIcon,
|
|
37
|
+
required,
|
|
38
|
+
style,
|
|
39
|
+
onPress,
|
|
40
|
+
componentId,
|
|
41
|
+
}) => {
|
|
42
|
+
const { theme } = useContext(ApplicationContext);
|
|
43
|
+
const scaledTop = useScaleSize(LABEL_OFFSET);
|
|
44
|
+
if (!floatingValue) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let floatingTextColor = theme.colors.text.hint;
|
|
49
|
+
let floatingIconTintColor = floatingIconColor;
|
|
50
|
+
if (disabled) {
|
|
51
|
+
floatingTextColor = theme.colors.text.disable;
|
|
52
|
+
floatingIconTintColor = theme.colors.text.disable;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<View
|
|
57
|
+
style={[
|
|
58
|
+
styles.floatingView,
|
|
59
|
+
styles.floatingViewLayer,
|
|
60
|
+
{
|
|
61
|
+
backgroundColor: theme.colors.background.surface,
|
|
62
|
+
top: -scaledTop,
|
|
63
|
+
},
|
|
64
|
+
style,
|
|
65
|
+
]}
|
|
66
|
+
>
|
|
67
|
+
<Text
|
|
68
|
+
color={floatingTextColor}
|
|
69
|
+
typography={'label_s_medium'}
|
|
70
|
+
accessibilityLabel={`${componentId}|floating-text`}
|
|
71
|
+
>
|
|
72
|
+
{floatingValue}
|
|
73
|
+
{required && (
|
|
74
|
+
<Text
|
|
75
|
+
typography={'label_s_medium'}
|
|
76
|
+
color={theme.colors.error.primary}
|
|
77
|
+
>
|
|
78
|
+
*
|
|
79
|
+
</Text>
|
|
80
|
+
)}
|
|
81
|
+
</Text>
|
|
82
|
+
{!!floatingIcon && (
|
|
83
|
+
<TouchableOpacity
|
|
84
|
+
activeOpacity={onPress ? PRESSED_OPACITY : 1}
|
|
85
|
+
onPress={onPress}
|
|
86
|
+
accessibilityLabel={`${componentId}|floating-icon-touch`}
|
|
87
|
+
>
|
|
88
|
+
<Icon
|
|
89
|
+
color={floatingIconTintColor}
|
|
90
|
+
source={floatingIcon}
|
|
91
|
+
size={ICON_SIZE}
|
|
92
|
+
style={styles.floatingIcon}
|
|
93
|
+
accessibilityLabel={`${componentId}|floating-icon`}
|
|
94
|
+
/>
|
|
95
|
+
</TouchableOpacity>
|
|
96
|
+
)}
|
|
97
|
+
</View>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export { FloatingView };
|
package/Input/Input.tsx
CHANGED
|
@@ -13,22 +13,25 @@ import {
|
|
|
13
13
|
} from 'react-native';
|
|
14
14
|
import { useComponentId } from '../Application';
|
|
15
15
|
import { Icon } from '../Icon';
|
|
16
|
-
import { exportFontFamily, useScaleSize } from '../Text';
|
|
17
|
-
import {
|
|
18
|
-
ErrorView,
|
|
19
|
-
FloatingView,
|
|
20
|
-
getBorderColor,
|
|
21
|
-
getSizeStyle,
|
|
22
|
-
RenderTrailing,
|
|
23
|
-
} from './common';
|
|
24
|
-
import { InputProps } from './index';
|
|
25
|
-
import styles from './styles';
|
|
26
|
-
import SystemTextInput from './SystemTextInput';
|
|
27
16
|
import {
|
|
28
17
|
ApplicationContext,
|
|
29
18
|
ComponentContext,
|
|
30
19
|
MiniAppContext,
|
|
31
20
|
} from '../Context';
|
|
21
|
+
import { exportFontFamily, useScaleSize } from '../Text';
|
|
22
|
+
import { CLEAR_ICON_SIZE, INPUT_FONT_SIZE } from './constants';
|
|
23
|
+
import { ErrorView } from './ErrorView';
|
|
24
|
+
import { FloatingView } from './FloatingView';
|
|
25
|
+
import { InputProps } from './index';
|
|
26
|
+
import { RenderTrailing } from './RenderTrailing';
|
|
27
|
+
import styles from './styles';
|
|
28
|
+
import SystemTextInput from './SystemTextInput';
|
|
29
|
+
import {
|
|
30
|
+
getBorderColor,
|
|
31
|
+
getInputHeight,
|
|
32
|
+
getLeadingIconSize,
|
|
33
|
+
getSizeStyle,
|
|
34
|
+
} from './utils';
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* Text input with floating label, leading/trailing/clear icons and an error or
|
|
@@ -73,11 +76,11 @@ const Input = forwardRef(
|
|
|
73
76
|
) => {
|
|
74
77
|
const { theme } = useContext(ApplicationContext);
|
|
75
78
|
const context = useContext<any>(MiniAppContext);
|
|
76
|
-
const scaledFontSize = useScaleSize(
|
|
77
|
-
const scaleHeight = useScaleSize(size
|
|
78
|
-
const [
|
|
79
|
+
const scaledFontSize = useScaleSize(INPUT_FONT_SIZE);
|
|
80
|
+
const scaleHeight = useScaleSize(getInputHeight(size));
|
|
81
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
79
82
|
const [internalText, setInternalText] = useState(defaultValue || '');
|
|
80
|
-
const
|
|
83
|
+
const hasValue = value !== undefined ? !!value : !!internalText;
|
|
81
84
|
const inputRef = useRef<TextInput | null>(null);
|
|
82
85
|
const componentName = 'Input';
|
|
83
86
|
|
|
@@ -88,30 +91,30 @@ const Input = forwardRef(
|
|
|
88
91
|
props.accessibilityLabel,
|
|
89
92
|
);
|
|
90
93
|
|
|
91
|
-
const
|
|
94
|
+
const handleClearText = () => {
|
|
92
95
|
inputRef?.current?.clear();
|
|
93
96
|
inputRef.current?.setNativeProps({ text: '' });
|
|
94
|
-
|
|
97
|
+
handleChangeText('');
|
|
95
98
|
};
|
|
96
99
|
|
|
97
|
-
const
|
|
100
|
+
const handleChangeText = (text: string) => {
|
|
98
101
|
if (value === undefined) {
|
|
99
102
|
setInternalText(text);
|
|
100
103
|
}
|
|
101
104
|
onChangeText?.(text);
|
|
102
105
|
};
|
|
103
106
|
|
|
104
|
-
const
|
|
105
|
-
|
|
107
|
+
const handleFocus = (e: TextInputFocusEvent) => {
|
|
108
|
+
setIsFocused(true);
|
|
106
109
|
onFocus?.(e);
|
|
107
110
|
};
|
|
108
111
|
|
|
109
|
-
const
|
|
110
|
-
|
|
112
|
+
const handleBlur = (e: TextInputFocusEvent) => {
|
|
113
|
+
setIsFocused(false);
|
|
111
114
|
onBlur?.(e);
|
|
112
115
|
};
|
|
113
116
|
|
|
114
|
-
const
|
|
117
|
+
const handleSetText = (text: string) => {
|
|
115
118
|
if (__DEV__ && value !== undefined) {
|
|
116
119
|
console.error(
|
|
117
120
|
`${componentName}: calling ref.setText() on a controlled input (\`value\` is provided) is an anti-pattern. ` +
|
|
@@ -120,21 +123,21 @@ const Input = forwardRef(
|
|
|
120
123
|
);
|
|
121
124
|
}
|
|
122
125
|
inputRef?.current?.setNativeProps({ text });
|
|
123
|
-
|
|
126
|
+
handleChangeText(text);
|
|
124
127
|
};
|
|
125
128
|
|
|
126
129
|
useImperativeHandle(ref, () => {
|
|
127
130
|
return {
|
|
128
|
-
clear:
|
|
131
|
+
clear: handleClearText,
|
|
129
132
|
focus: () => inputRef.current?.focus(),
|
|
130
133
|
blur: () => inputRef.current?.blur(),
|
|
131
|
-
setText:
|
|
134
|
+
setText: handleSetText,
|
|
132
135
|
};
|
|
133
136
|
});
|
|
134
137
|
|
|
135
138
|
const renderInputView = () => {
|
|
136
139
|
const disabledColor = theme.colors.text.disable;
|
|
137
|
-
const
|
|
140
|
+
const isSecure = secureTextEntry;
|
|
138
141
|
let textColor = theme.colors.text.default;
|
|
139
142
|
let placeholderColor = theme.colors.text.hint;
|
|
140
143
|
let iconTintColor = trailingColor;
|
|
@@ -156,7 +159,7 @@ const Input = forwardRef(
|
|
|
156
159
|
styles.inputWrapper,
|
|
157
160
|
{ backgroundColor: theme.colors.background.surface },
|
|
158
161
|
getSizeStyle(size, undefined, scaleHeight),
|
|
159
|
-
getBorderColor(theme,
|
|
162
|
+
getBorderColor(theme, isFocused, errorMessage, disabled),
|
|
160
163
|
]}
|
|
161
164
|
>
|
|
162
165
|
<FloatingView
|
|
@@ -178,7 +181,7 @@ const Input = forwardRef(
|
|
|
178
181
|
<Icon
|
|
179
182
|
color={leadingIconColor}
|
|
180
183
|
source={leadingIcon}
|
|
181
|
-
size={size
|
|
184
|
+
size={getLeadingIconSize(size)}
|
|
182
185
|
accessibilityLabel={`${componentId}|leading-icon`}
|
|
183
186
|
/>
|
|
184
187
|
</TouchableOpacity>
|
|
@@ -188,7 +191,7 @@ const Input = forwardRef(
|
|
|
188
191
|
{...props}
|
|
189
192
|
accessibilityLabel={`${componentId}`}
|
|
190
193
|
editable={editable && !disabled}
|
|
191
|
-
secureTextEntry={
|
|
194
|
+
secureTextEntry={isSecure}
|
|
192
195
|
textAlignVertical="center"
|
|
193
196
|
ref={inputRef}
|
|
194
197
|
accessibilityState={{
|
|
@@ -202,15 +205,15 @@ const Input = forwardRef(
|
|
|
202
205
|
color: textColor,
|
|
203
206
|
fontFamily: exportFontFamily(fontWeight ?? 'regular'),
|
|
204
207
|
},
|
|
205
|
-
|
|
208
|
+
isSecure && hasValue && secureTextStyle,
|
|
206
209
|
]}
|
|
207
210
|
defaultValue={defaultValue}
|
|
208
211
|
allowFontScaling={false}
|
|
209
212
|
textBreakStrategy="highQuality"
|
|
210
213
|
value={value}
|
|
211
|
-
onChangeText={
|
|
212
|
-
onFocus={
|
|
213
|
-
onBlur={
|
|
214
|
+
onChangeText={handleChangeText}
|
|
215
|
+
onFocus={handleFocus}
|
|
216
|
+
onBlur={handleBlur}
|
|
214
217
|
placeholder={placeholder}
|
|
215
218
|
selectionColor={theme.colors.primary}
|
|
216
219
|
placeholderTextColor={placeholderColor}
|
|
@@ -218,16 +221,16 @@ const Input = forwardRef(
|
|
|
218
221
|
/>
|
|
219
222
|
</View>
|
|
220
223
|
<View style={styles.iconView}>
|
|
221
|
-
{showClearIcon &&
|
|
224
|
+
{showClearIcon && isFocused && hasValue && (
|
|
222
225
|
<TouchableOpacity
|
|
223
226
|
accessibilityLabel={`${componentId}|clear-icon-touch`}
|
|
224
227
|
accessible={false}
|
|
225
228
|
style={styles.iconWrapper}
|
|
226
|
-
onPress={
|
|
229
|
+
onPress={handleClearText}
|
|
227
230
|
>
|
|
228
231
|
<Icon
|
|
229
232
|
source="24_navigation_close_circle_full"
|
|
230
|
-
size={
|
|
233
|
+
size={CLEAR_ICON_SIZE}
|
|
231
234
|
color={theme.colors.text.hint}
|
|
232
235
|
accessibilityLabel={`${componentId}|clear-icon`}
|
|
233
236
|
/>
|
package/Input/InputDropDown.tsx
CHANGED
|
@@ -6,17 +6,20 @@ import {
|
|
|
6
6
|
ComponentContext,
|
|
7
7
|
MiniAppContext,
|
|
8
8
|
} from '../Context';
|
|
9
|
+
import { Spacing } from '../Consts';
|
|
9
10
|
import { Icon } from '../Icon';
|
|
11
|
+
import { Text, useScaleSize } from '../Text';
|
|
12
|
+
import { PRESSED_OPACITY } from './constants';
|
|
13
|
+
import { ErrorView } from './ErrorView';
|
|
14
|
+
import { FloatingView } from './FloatingView';
|
|
15
|
+
import { InputDropDownProps } from './index';
|
|
16
|
+
import styles from './styles';
|
|
10
17
|
import {
|
|
11
|
-
ErrorView,
|
|
12
|
-
FloatingView,
|
|
13
18
|
getBorderColor,
|
|
19
|
+
getInputHeight,
|
|
20
|
+
getLeadingIconSize,
|
|
14
21
|
getSizeStyle,
|
|
15
|
-
} from './
|
|
16
|
-
import { InputDropDownProps } from './index';
|
|
17
|
-
import { Text, useScaleSize } from '../Text';
|
|
18
|
-
import styles from './styles';
|
|
19
|
-
import { Spacing } from '../Consts';
|
|
22
|
+
} from './utils';
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* Pressable field styled like an input with a chevron; shows `value` or
|
|
@@ -44,7 +47,7 @@ const InputDropDown = ({
|
|
|
44
47
|
}: InputDropDownProps) => {
|
|
45
48
|
const { theme } = useContext(ApplicationContext);
|
|
46
49
|
const context = useContext<any>(MiniAppContext);
|
|
47
|
-
const scaleHeight = useScaleSize(size
|
|
50
|
+
const scaleHeight = useScaleSize(getInputHeight(size));
|
|
48
51
|
const componentName = 'InputDropDown';
|
|
49
52
|
|
|
50
53
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
@@ -99,7 +102,7 @@ const InputDropDown = ({
|
|
|
99
102
|
<Icon
|
|
100
103
|
color={leadingIconColor}
|
|
101
104
|
source={leadingIcon}
|
|
102
|
-
size={size
|
|
105
|
+
size={getLeadingIconSize(size)}
|
|
103
106
|
accessibilityLabel={`${componentId}|leading-icon`}
|
|
104
107
|
/>
|
|
105
108
|
</View>
|
|
@@ -136,7 +139,7 @@ const InputDropDown = ({
|
|
|
136
139
|
{...props}
|
|
137
140
|
accessibilityLabel={`${componentId}|touch`}
|
|
138
141
|
accessibilityState={{ disabled: disabled as boolean }}
|
|
139
|
-
activeOpacity={
|
|
142
|
+
activeOpacity={PRESSED_OPACITY}
|
|
140
143
|
style={[
|
|
141
144
|
style,
|
|
142
145
|
styles.wrapper,
|
package/Input/InputMoney.tsx
CHANGED
|
@@ -10,11 +10,11 @@ import React, {
|
|
|
10
10
|
import { Platform, TextInput, TextInputFocusEvent, View } from 'react-native';
|
|
11
11
|
import { useComponentId } from '../Application';
|
|
12
12
|
import { ComponentContext, MiniAppContext } from '../Context';
|
|
13
|
-
import { ErrorView } from './
|
|
13
|
+
import { ErrorView } from './ErrorView';
|
|
14
14
|
import { InputMoneyProps } from './index';
|
|
15
15
|
import { InputMoneyView } from './InputMoneyView';
|
|
16
|
-
import { findEdit, moneyFormatter } from './moneyUtils';
|
|
17
16
|
import styles from './styles';
|
|
17
|
+
import { findEdit, moneyFormatter } from './utils';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Money input: keeps digits only, shows them grouped with a currency suffix and
|
|
@@ -52,7 +52,7 @@ const InputMoney = forwardRef(
|
|
|
52
52
|
ref,
|
|
53
53
|
) => {
|
|
54
54
|
const context = useContext<any>(MiniAppContext);
|
|
55
|
-
const [
|
|
55
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
56
56
|
const [displayValue, setDisplayValue] = useState('');
|
|
57
57
|
const [numericValue, setNumericValue] = useState('');
|
|
58
58
|
const [lastDisplayValue, setLastDisplayValue] = useState('');
|
|
@@ -126,7 +126,7 @@ const InputMoney = forwardRef(
|
|
|
126
126
|
}
|
|
127
127
|
};
|
|
128
128
|
|
|
129
|
-
const
|
|
129
|
+
const handleClearText = () => {
|
|
130
130
|
inputRef?.current?.clear();
|
|
131
131
|
inputRef.current?.setNativeProps({ text: '' });
|
|
132
132
|
setDisplayValue('');
|
|
@@ -134,7 +134,7 @@ const InputMoney = forwardRef(
|
|
|
134
134
|
onChangeText?.('');
|
|
135
135
|
};
|
|
136
136
|
|
|
137
|
-
const
|
|
137
|
+
const handleChangeText = (text: string) => {
|
|
138
138
|
if (!text) {
|
|
139
139
|
lastRawText.current = '';
|
|
140
140
|
setDisplayValue('');
|
|
@@ -166,17 +166,17 @@ const InputMoney = forwardRef(
|
|
|
166
166
|
inputRef.current?.setSelection(displayValue.length, displayValue.length);
|
|
167
167
|
};
|
|
168
168
|
|
|
169
|
-
const
|
|
170
|
-
|
|
169
|
+
const handleFocus = (e: TextInputFocusEvent) => {
|
|
170
|
+
setIsFocused(true);
|
|
171
171
|
onFocus?.(e);
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
-
const
|
|
175
|
-
|
|
174
|
+
const handleBlur = (e: TextInputFocusEvent) => {
|
|
175
|
+
setIsFocused(false);
|
|
176
176
|
onBlur?.(e);
|
|
177
177
|
};
|
|
178
178
|
|
|
179
|
-
const
|
|
179
|
+
const handleSetText = (text: string) => {
|
|
180
180
|
caretDigitsAfter.current = undefined;
|
|
181
181
|
if (!text) {
|
|
182
182
|
setDisplayValue('');
|
|
@@ -190,10 +190,10 @@ const InputMoney = forwardRef(
|
|
|
190
190
|
|
|
191
191
|
useImperativeHandle(ref, () => {
|
|
192
192
|
return {
|
|
193
|
-
clear:
|
|
193
|
+
clear: handleClearText,
|
|
194
194
|
focus: () => inputRef.current?.focus(),
|
|
195
195
|
blur: () => inputRef.current?.blur(),
|
|
196
|
-
setText:
|
|
196
|
+
setText: handleSetText,
|
|
197
197
|
};
|
|
198
198
|
});
|
|
199
199
|
|
|
@@ -240,16 +240,16 @@ const InputMoney = forwardRef(
|
|
|
240
240
|
showClearIcon={showClearIcon}
|
|
241
241
|
placeholder={placeholder}
|
|
242
242
|
currency={currency}
|
|
243
|
-
focused={
|
|
243
|
+
focused={isFocused}
|
|
244
244
|
componentId={componentId}
|
|
245
245
|
displayValue={displayValue}
|
|
246
246
|
caretSelection={caretSelection}
|
|
247
247
|
inputRef={inputRef}
|
|
248
248
|
focusAtEnd={focusAtEnd}
|
|
249
|
-
onClearText={
|
|
250
|
-
onChangeText={
|
|
251
|
-
onFocus={
|
|
252
|
-
onBlur={
|
|
249
|
+
onClearText={handleClearText}
|
|
250
|
+
onChangeText={handleChangeText}
|
|
251
|
+
onFocus={handleFocus}
|
|
252
|
+
onBlur={handleBlur}
|
|
253
253
|
inputProps={props}
|
|
254
254
|
/>
|
|
255
255
|
<ErrorView
|
package/Input/InputMoneyView.tsx
CHANGED
|
@@ -11,15 +11,15 @@ import {
|
|
|
11
11
|
import { ApplicationContext } from '../Context';
|
|
12
12
|
import { Icon } from '../Icon';
|
|
13
13
|
import { useScaleSize, exportFontFamily } from '../Text';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
getBorderColor,
|
|
17
|
-
getSizeStyle,
|
|
18
|
-
RenderTrailing,
|
|
19
|
-
} from './common';
|
|
14
|
+
import { CLEAR_ICON_SIZE } from './constants';
|
|
15
|
+
import { FloatingView } from './FloatingView';
|
|
20
16
|
import { InputMoneyProps } from './index';
|
|
17
|
+
import { RenderTrailing } from './RenderTrailing';
|
|
21
18
|
import styles from './styles';
|
|
22
19
|
import SystemTextInput from './SystemTextInput';
|
|
20
|
+
import { getBorderColor, getInputHeight, getSizeStyle } from './utils';
|
|
21
|
+
|
|
22
|
+
const MONEY_FONT_SIZE = 20;
|
|
23
23
|
|
|
24
24
|
type InputMoneyViewProps = Pick<
|
|
25
25
|
InputMoneyProps,
|
|
@@ -85,8 +85,8 @@ export const InputMoneyView = ({
|
|
|
85
85
|
inputProps: props,
|
|
86
86
|
}: InputMoneyViewProps) => {
|
|
87
87
|
const { theme } = useContext(ApplicationContext);
|
|
88
|
-
const scaleHeight = useScaleSize(size
|
|
89
|
-
const scaledFontSize = useScaleSize(
|
|
88
|
+
const scaleHeight = useScaleSize(getInputHeight(size));
|
|
89
|
+
const scaledFontSize = useScaleSize(MONEY_FONT_SIZE);
|
|
90
90
|
const inputFontFamily = exportFontFamily('bold');
|
|
91
91
|
const disabledColor = theme.colors.text.disable;
|
|
92
92
|
let textColor = theme.colors.text.default;
|
|
@@ -101,11 +101,11 @@ export const InputMoneyView = ({
|
|
|
101
101
|
fontSize: scaledFontSize,
|
|
102
102
|
fontFamily: inputFontFamily,
|
|
103
103
|
};
|
|
104
|
-
const
|
|
105
|
-
const shouldShowCurrency = !!displayValue ||
|
|
104
|
+
const hasCurrencyInPlaceholder = placeholder.endsWith(currency);
|
|
105
|
+
const shouldShowCurrency = !!displayValue || hasCurrencyInPlaceholder;
|
|
106
106
|
const shouldShowClearIcon = !!(showClearIcon && focused && displayValue);
|
|
107
107
|
let inputPlaceholder = placeholder;
|
|
108
|
-
if (
|
|
108
|
+
if (hasCurrencyInPlaceholder) {
|
|
109
109
|
inputPlaceholder = placeholder.slice(0, -currency.length);
|
|
110
110
|
}
|
|
111
111
|
return (
|
|
@@ -175,7 +175,7 @@ export const InputMoneyView = ({
|
|
|
175
175
|
>
|
|
176
176
|
<Icon
|
|
177
177
|
source="24_navigation_close_circle_full"
|
|
178
|
-
size={
|
|
178
|
+
size={CLEAR_ICON_SIZE}
|
|
179
179
|
color={theme.colors.text.hint}
|
|
180
180
|
accessibilityLabel={`${componentId}|clear-icon`}
|
|
181
181
|
/>
|
package/Input/InputOTP.tsx
CHANGED
|
@@ -26,31 +26,37 @@ import Animated, {
|
|
|
26
26
|
} from 'react-native-reanimated';
|
|
27
27
|
import { useComponentId } from '../Application';
|
|
28
28
|
import { Spacing, Styles } from '../Consts';
|
|
29
|
-
import { useScaleSize, Text } from '../Text';
|
|
30
|
-
import { ErrorView, getBorderColor } from './common';
|
|
31
|
-
import { CaretProps, InputOTPProps } from './index';
|
|
32
|
-
import styles from './styles';
|
|
33
|
-
import { Icon } from '../Icon';
|
|
34
|
-
import SystemTextInput from './SystemTextInput';
|
|
35
29
|
import {
|
|
36
30
|
ApplicationContext,
|
|
37
31
|
ComponentContext,
|
|
38
32
|
MiniAppContext,
|
|
39
33
|
} from '../Context';
|
|
34
|
+
import { Icon } from '../Icon';
|
|
35
|
+
import { useScaleSize, Text } from '../Text';
|
|
36
|
+
import { ErrorView } from './ErrorView';
|
|
37
|
+
import { CaretProps, InputOTPProps } from './index';
|
|
38
|
+
import styles from './styles';
|
|
39
|
+
import SystemTextInput from './SystemTextInput';
|
|
40
|
+
import { getBorderColor } from './utils';
|
|
41
|
+
|
|
42
|
+
const BLINK_DURATION = 300;
|
|
43
|
+
const CARET_HEIGHT = 12;
|
|
44
|
+
const OTP_CLEAR_ICON_SIZE = 12;
|
|
45
|
+
const MAX_LENGTH = 10;
|
|
46
|
+
const COMPACT_LENGTH_THRESHOLD = 6;
|
|
40
47
|
|
|
41
48
|
/** Blinking caret shown in the OTP slot being typed. */
|
|
42
49
|
const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
43
|
-
const DURATION = 300;
|
|
44
50
|
const { theme } = useContext(ApplicationContext);
|
|
45
51
|
const opacity = useSharedValue(0);
|
|
46
52
|
|
|
47
53
|
useEffect(() => {
|
|
48
54
|
opacity.value = withRepeat(
|
|
49
55
|
withSequence(
|
|
50
|
-
withTiming(1, { duration:
|
|
56
|
+
withTiming(1, { duration: BLINK_DURATION, easing: Easing.linear }),
|
|
51
57
|
withDelay(
|
|
52
|
-
|
|
53
|
-
withTiming(0, { duration:
|
|
58
|
+
BLINK_DURATION * 2,
|
|
59
|
+
withTiming(0, { duration: BLINK_DURATION, easing: Easing.linear }),
|
|
54
60
|
),
|
|
55
61
|
),
|
|
56
62
|
-1,
|
|
@@ -72,7 +78,7 @@ const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
|
72
78
|
style={[
|
|
73
79
|
styles.otpCursor,
|
|
74
80
|
{
|
|
75
|
-
height: useScaleSize(
|
|
81
|
+
height: useScaleSize(CARET_HEIGHT),
|
|
76
82
|
backgroundColor: theme.colors.primary,
|
|
77
83
|
},
|
|
78
84
|
animatedStyle,
|
|
@@ -108,9 +114,8 @@ const InputOTP = forwardRef(
|
|
|
108
114
|
}: InputOTPProps,
|
|
109
115
|
ref,
|
|
110
116
|
) => {
|
|
111
|
-
const MAX_LENGTH = 10;
|
|
112
117
|
const [value, setValue] = useState('');
|
|
113
|
-
const [
|
|
118
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
114
119
|
const inputRef = useRef<TextInput | null>(null);
|
|
115
120
|
const { theme } = useContext(ApplicationContext);
|
|
116
121
|
const context = useContext<any>(MiniAppContext);
|
|
@@ -127,25 +132,25 @@ const InputOTP = forwardRef(
|
|
|
127
132
|
useImperativeHandle(ref, () => {
|
|
128
133
|
return {
|
|
129
134
|
onChangeText: (text: string) => {
|
|
130
|
-
|
|
135
|
+
handleChangeText(text);
|
|
131
136
|
},
|
|
132
137
|
focus: () => inputRef.current?.focus(),
|
|
133
138
|
blur: () => inputRef.current?.blur(),
|
|
134
|
-
setText: (text: string) =>
|
|
139
|
+
setText: (text: string) => handleChangeText(text),
|
|
135
140
|
};
|
|
136
141
|
});
|
|
137
142
|
|
|
138
|
-
const
|
|
139
|
-
|
|
143
|
+
const handleFocusInput = (e: TextInputFocusEvent) => {
|
|
144
|
+
setIsFocused(true);
|
|
140
145
|
onFocus?.(e);
|
|
141
146
|
};
|
|
142
147
|
|
|
143
|
-
const
|
|
144
|
-
|
|
148
|
+
const handleBlurInput = (e: TextInputFocusEvent) => {
|
|
149
|
+
setIsFocused(false);
|
|
145
150
|
onBlur?.(e);
|
|
146
151
|
};
|
|
147
152
|
|
|
148
|
-
const
|
|
153
|
+
const handleChangeText = (text: string) => {
|
|
149
154
|
if (text.length > MAX_LENGTH) return;
|
|
150
155
|
const isNumeric = !isNaN(Number(text));
|
|
151
156
|
if (dataType === 'number' && !isNumeric) return;
|
|
@@ -156,8 +161,8 @@ const InputOTP = forwardRef(
|
|
|
156
161
|
onChangeText?.(text);
|
|
157
162
|
};
|
|
158
163
|
|
|
159
|
-
const
|
|
160
|
-
|
|
164
|
+
const handleClearText = () => {
|
|
165
|
+
handleChangeText('');
|
|
161
166
|
};
|
|
162
167
|
|
|
163
168
|
const fontScale = PixelRatio.getFontScale();
|
|
@@ -167,7 +172,7 @@ const InputOTP = forwardRef(
|
|
|
167
172
|
const textColors = theme.colors.text;
|
|
168
173
|
for (let i = 0; i < inputLength; i++) {
|
|
169
174
|
let slot: React.ReactNode;
|
|
170
|
-
if (
|
|
175
|
+
if (isFocused && value.length === i) {
|
|
171
176
|
slot = <OTPCaret index={i} length={inputLength} />;
|
|
172
177
|
} else {
|
|
173
178
|
slot = (
|
|
@@ -188,7 +193,7 @@ const InputOTP = forwardRef(
|
|
|
188
193
|
const renderUnidentifiedInputs = () => {
|
|
189
194
|
const splitValue = value.split('');
|
|
190
195
|
|
|
191
|
-
if (!
|
|
196
|
+
if (!isFocused && value.length === 0 && !!placeholder) {
|
|
192
197
|
return (
|
|
193
198
|
<Text
|
|
194
199
|
color={theme.colors.text.hint}
|
|
@@ -203,8 +208,9 @@ const InputOTP = forwardRef(
|
|
|
203
208
|
<View style={Styles.row}>
|
|
204
209
|
{splitValue.map((letter, index) => {
|
|
205
210
|
const isLast = index === value.length - 1;
|
|
206
|
-
const isTyping =
|
|
207
|
-
const
|
|
211
|
+
const isTyping = isFocused && value.length !== MAX_LENGTH;
|
|
212
|
+
const isLongCode = value.length > COMPACT_LENGTH_THRESHOLD;
|
|
213
|
+
const isCompact = fontScale > 1 && isLongCode;
|
|
208
214
|
const marginRight = isCompact ? Spacing.M : Spacing.L;
|
|
209
215
|
return (
|
|
210
216
|
<Text
|
|
@@ -216,7 +222,7 @@ const InputOTP = forwardRef(
|
|
|
216
222
|
</Text>
|
|
217
223
|
);
|
|
218
224
|
})}
|
|
219
|
-
{
|
|
225
|
+
{isFocused && value.length !== MAX_LENGTH && <OTPCaret />}
|
|
220
226
|
</View>
|
|
221
227
|
);
|
|
222
228
|
};
|
|
@@ -229,7 +235,7 @@ const InputOTP = forwardRef(
|
|
|
229
235
|
style={[
|
|
230
236
|
styles.otpInput,
|
|
231
237
|
{ backgroundColor: theme.colors.background.surface },
|
|
232
|
-
getBorderColor(theme,
|
|
238
|
+
getBorderColor(theme, isFocused, errorMessage),
|
|
233
239
|
]}
|
|
234
240
|
>
|
|
235
241
|
{!!floatingValue && (
|
|
@@ -248,9 +254,15 @@ const InputOTP = forwardRef(
|
|
|
248
254
|
</Text>
|
|
249
255
|
</View>
|
|
250
256
|
)}
|
|
251
|
-
{value.length > 0 &&
|
|
252
|
-
<TouchableOpacity
|
|
253
|
-
|
|
257
|
+
{value.length > 0 && isFocused && (
|
|
258
|
+
<TouchableOpacity
|
|
259
|
+
onPress={handleClearText}
|
|
260
|
+
style={styles.clearIcon}
|
|
261
|
+
>
|
|
262
|
+
<Icon
|
|
263
|
+
source={'24_navigation_close_circle_full'}
|
|
264
|
+
size={OTP_CLEAR_ICON_SIZE}
|
|
265
|
+
/>
|
|
254
266
|
</TouchableOpacity>
|
|
255
267
|
)}
|
|
256
268
|
<View style={styles.otpInputsView}>
|
|
@@ -262,11 +274,11 @@ const InputOTP = forwardRef(
|
|
|
262
274
|
ref={inputRef}
|
|
263
275
|
value={value}
|
|
264
276
|
textAlign={'center'}
|
|
265
|
-
onChangeText={
|
|
277
|
+
onChangeText={handleChangeText}
|
|
266
278
|
keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
|
|
267
279
|
allowFontScaling={false}
|
|
268
|
-
onFocus={
|
|
269
|
-
onBlur={
|
|
280
|
+
onFocus={handleFocusInput}
|
|
281
|
+
onBlur={handleBlurInput}
|
|
270
282
|
style={styles.otpRealInput}
|
|
271
283
|
selectionColor={theme.colors.primary}
|
|
272
284
|
placeholderTextColor={theme.colors.text.hint}
|