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