@momo-kits/foundation 0.74.2-react-native.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.
Files changed (105) hide show
  1. package/Application/BottomTab.tsx +120 -0
  2. package/Application/Components.tsx +534 -0
  3. package/Application/ModalScreen.tsx +268 -0
  4. package/Application/Navigation.ts +50 -0
  5. package/Application/NavigationContainer.tsx +120 -0
  6. package/Application/Navigator.ts +131 -0
  7. package/Application/StackScreen.tsx +71 -0
  8. package/Application/index.ts +41 -0
  9. package/Application/types.ts +216 -0
  10. package/Application/utils.tsx +151 -0
  11. package/Assets/DotAnimation.json +256 -0
  12. package/Assets/SpinnerAnimation.json +1027 -0
  13. package/Assets/icon.json +4031 -0
  14. package/Assets/icon_bank.json +506 -0
  15. package/Assets/language.json +202 -0
  16. package/Badge/Badge.tsx +56 -0
  17. package/Badge/BadgeDot.tsx +11 -0
  18. package/Badge/BadgeRibbon.tsx +125 -0
  19. package/Badge/Shape.tsx +29 -0
  20. package/Badge/index.tsx +7 -0
  21. package/Badge/styles.ts +58 -0
  22. package/Badge/types.ts +63 -0
  23. package/Button/index.tsx +310 -0
  24. package/Button/styles.ts +60 -0
  25. package/CheckBox/index.tsx +65 -0
  26. package/CheckBox/styles.ts +15 -0
  27. package/CheckBox/types.ts +38 -0
  28. package/Consts/colors+spacing+radius.ts +218 -0
  29. package/Consts/index.ts +4 -0
  30. package/Consts/styles.ts +52 -0
  31. package/Consts/theme.ts +125 -0
  32. package/Divider/index.tsx +21 -0
  33. package/Icon/index.tsx +41 -0
  34. package/Icon/types.ts +27 -0
  35. package/IconButton/index.tsx +139 -0
  36. package/IconButton/styles.ts +19 -0
  37. package/Image/index.tsx +83 -0
  38. package/Image/styles.ts +5 -0
  39. package/Image/types.ts +5 -0
  40. package/Input/Input.tsx +192 -0
  41. package/Input/InputDropDown.tsx +114 -0
  42. package/Input/InputMoney.tsx +163 -0
  43. package/Input/InputOTP.tsx +235 -0
  44. package/Input/InputSearch.tsx +151 -0
  45. package/Input/InputTextArea.tsx +159 -0
  46. package/Input/common.tsx +129 -0
  47. package/Input/index.tsx +251 -0
  48. package/Input/styles.ts +160 -0
  49. package/Input/utils.ts +69 -0
  50. package/Layout/Card.tsx +180 -0
  51. package/Layout/GridSystem.tsx +136 -0
  52. package/Layout/Item.tsx +56 -0
  53. package/Layout/ItemList.tsx +55 -0
  54. package/Layout/ItemSectionList.tsx +39 -0
  55. package/Layout/Screen.tsx +355 -0
  56. package/Layout/Section.tsx +179 -0
  57. package/Layout/index.ts +29 -0
  58. package/Layout/styles.ts +97 -0
  59. package/Layout/types.ts +23 -0
  60. package/Layout/utils.ts +55 -0
  61. package/Loader/DotLoader.tsx +37 -0
  62. package/Loader/Loader.tsx +13 -0
  63. package/Loader/ProgressBar.tsx +44 -0
  64. package/Loader/Spinner.tsx +28 -0
  65. package/Loader/index.tsx +4 -0
  66. package/Loader/styles.ts +10 -0
  67. package/Loader/types.ts +35 -0
  68. package/Loader/utils.ts +25 -0
  69. package/Pagination/Dot.tsx +16 -0
  70. package/Pagination/PaginationDot.tsx +38 -0
  71. package/Pagination/PaginationNumber.tsx +24 -0
  72. package/Pagination/PaginationScroll.tsx +88 -0
  73. package/Pagination/PaginationWhiteDot.tsx +35 -0
  74. package/Pagination/index.tsx +25 -0
  75. package/Pagination/styles.ts +50 -0
  76. package/Pagination/types.ts +40 -0
  77. package/Popup/PopupNotify.tsx +266 -0
  78. package/Popup/PopupPromotion.tsx +120 -0
  79. package/Popup/index.tsx +4 -0
  80. package/Popup/types.ts +87 -0
  81. package/Radio/index.tsx +64 -0
  82. package/Radio/styles.ts +11 -0
  83. package/Radio/types.ts +38 -0
  84. package/Skeleton/index.tsx +72 -0
  85. package/Skeleton/styles.ts +5 -0
  86. package/Skeleton/types.ts +6 -0
  87. package/Switch/index.tsx +37 -0
  88. package/Switch/styles.ts +23 -0
  89. package/Switch/types.ts +26 -0
  90. package/Tag/index.tsx +79 -0
  91. package/Tag/styles.ts +24 -0
  92. package/Tag/types.ts +36 -0
  93. package/Text/index.tsx +110 -0
  94. package/Text/styles.ts +73 -0
  95. package/Text/types.ts +34 -0
  96. package/Text/utils.ts +14 -0
  97. package/Title/index.tsx +132 -0
  98. package/Title/styles.ts +52 -0
  99. package/Title/types.ts +52 -0
  100. package/code-scanner.js +51 -0
  101. package/index.ts +51 -0
  102. package/package.json +36 -0
  103. package/publish.sh +23 -0
  104. package/tsconfig.json +24 -0
  105. package/verify.js +44 -0
@@ -0,0 +1,192 @@
1
+ import React, {forwardRef, useContext, useRef, useState} from 'react';
2
+ import {
3
+ NativeSyntheticEvent,
4
+ TextInput,
5
+ TextInputFocusEventData,
6
+ TouchableOpacity,
7
+ View,
8
+ } from 'react-native';
9
+ import {ApplicationContext} from '../Application';
10
+ import styles from './styles';
11
+ import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
12
+ import {InputProps} from './index';
13
+ import {Icon} from '../Icon';
14
+ import {checkTyping} from './utils';
15
+ import {Loader} from '../Loader';
16
+
17
+ const Input = forwardRef(
18
+ (
19
+ {
20
+ value,
21
+ onChangeText,
22
+ floatingValue,
23
+ floatingIcon,
24
+ size = 'small',
25
+ placeholder,
26
+ onBlur,
27
+ onFocus,
28
+ errorMessage,
29
+ icon,
30
+ disabled = false,
31
+ floatingIconColor,
32
+ iconColor,
33
+ required = false,
34
+ errorSpacing,
35
+ loading = false,
36
+ leadingIcon,
37
+ leadingIconColor,
38
+ fontWeight = 'Regular',
39
+ onPressIcon,
40
+ secureTextEntry,
41
+ style,
42
+ ...props
43
+ }: InputProps,
44
+ ref,
45
+ ) => {
46
+ const {theme} = useContext(ApplicationContext);
47
+ const [focused, setFocused] = useState(false);
48
+ const [haveValue, setHaveValue] = useState(!!value || !!props.defaultValue);
49
+ const [secureTextInput, setSecureTextInput] = useState(secureTextEntry);
50
+ const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
51
+
52
+ const onClearText = () => {
53
+ inputRef?.current?.clear();
54
+ _onChangeText('');
55
+ };
56
+
57
+ const _onChangeText = (text: string) => {
58
+ checkTyping(text, haveValue, setHaveValue);
59
+ onChangeText?.(text);
60
+ };
61
+
62
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
63
+ setFocused(true);
64
+ onFocus?.(e);
65
+ };
66
+
67
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
68
+ setFocused(false);
69
+ onBlur?.(e);
70
+ };
71
+
72
+ const renderIcon = (color: string | undefined) => {
73
+ if (loading) {
74
+ return <Loader type={'spinner'} color={color} style={styles.icon} />;
75
+ }
76
+ if (secureTextEntry) {
77
+ return (
78
+ <TouchableOpacity
79
+ onPress={() => setSecureTextInput(!secureTextInput)}
80
+ style={styles.icon}>
81
+ <Icon
82
+ color={color}
83
+ size={24}
84
+ source={
85
+ secureTextInput ? '24_security_eye_off' : '24_security_eye_open'
86
+ }
87
+ />
88
+ </TouchableOpacity>
89
+ );
90
+ }
91
+ if (icon) {
92
+ return (
93
+ <TouchableOpacity
94
+ onPress={onPressIcon}
95
+ disabled={!onPressIcon}
96
+ style={styles.icon}>
97
+ <Icon color={color} source={icon} size={24} />
98
+ </TouchableOpacity>
99
+ );
100
+ }
101
+ };
102
+
103
+ const renderInputView = () => {
104
+ const disabledColor = theme.colors.text.disable;
105
+ const secure = secureTextInput && secureTextEntry;
106
+ let textColor = theme.colors.text.default;
107
+ let placeholderColor = theme.colors.text.hint;
108
+ let iconTintColor = iconColor;
109
+
110
+ if (disabled) {
111
+ textColor = disabledColor;
112
+ placeholderColor = disabledColor;
113
+ iconTintColor = disabledColor;
114
+ }
115
+
116
+ return (
117
+ <View
118
+ style={[
119
+ styles.inputWrapper,
120
+ {backgroundColor: theme.colors.background.surface},
121
+ getSizeStyle(size),
122
+ getBorderColor(theme, focused, errorMessage, disabled),
123
+ ]}>
124
+ <FloatingView
125
+ floatingValue={floatingValue}
126
+ floatingIconColor={floatingIconColor}
127
+ disabled={disabled}
128
+ required={required}
129
+ floatingIcon={floatingIcon}
130
+ />
131
+ <View style={styles.inputView}>
132
+ {!!leadingIcon && (
133
+ <View style={styles.leadingIconContainer}>
134
+ <Icon
135
+ color={leadingIconColor}
136
+ source={leadingIcon}
137
+ size={size === 'small' ? 24 : 32}
138
+ />
139
+ </View>
140
+ )}
141
+ <TextInput
142
+ {...props}
143
+ editable={!disabled}
144
+ secureTextEntry={secure}
145
+ textAlignVertical="center"
146
+ ref={inputRef}
147
+ style={[
148
+ styles.input,
149
+ {
150
+ color: textColor,
151
+ fontFamily: `${theme.font}-${fontWeight}`,
152
+ },
153
+ secure && haveValue && {fontSize: size === 'small' ? 18 : 22},
154
+ ]}
155
+ textBreakStrategy="highQuality"
156
+ value={value}
157
+ onChangeText={_onChangeText}
158
+ onFocus={_onFocus}
159
+ onBlur={_onBlur}
160
+ placeholder={placeholder}
161
+ selectionColor={theme.colors.primary}
162
+ placeholderTextColor={placeholderColor}
163
+ />
164
+ </View>
165
+ <View style={styles.iconView}>
166
+ {focused && haveValue && (
167
+ <TouchableOpacity
168
+ style={styles.iconWrapper}
169
+ onPress={onClearText}>
170
+ <Icon
171
+ source="24_navigation_close_circle_full"
172
+ size={16}
173
+ color={theme.colors.text.hint}
174
+ />
175
+ </TouchableOpacity>
176
+ )}
177
+ {renderIcon(iconTintColor)}
178
+ </View>
179
+ </View>
180
+ );
181
+ };
182
+
183
+ return (
184
+ <View style={[style, styles.wrapper]}>
185
+ {renderInputView()}
186
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
187
+ </View>
188
+ );
189
+ },
190
+ );
191
+
192
+ export default Input;
@@ -0,0 +1,114 @@
1
+ import React, {useContext} from 'react';
2
+ import {TextInput, TouchableOpacity, View} from 'react-native';
3
+ import {ApplicationContext} from '../Application';
4
+ import styles from './styles';
5
+ import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
6
+ import {InputDropDownProps} from './index';
7
+ import {Icon} from '../Icon';
8
+ import {Loader} from '../Loader';
9
+ import {Styles} from '../Consts';
10
+
11
+ const InputDropDown = ({
12
+ value,
13
+ floatingValue,
14
+ floatingIcon,
15
+ size = 'small',
16
+ onPress,
17
+ placeholder,
18
+ errorMessage,
19
+ icon = 'arrow_chevron_down_small',
20
+ disabled = false,
21
+ floatingIconColor,
22
+ iconColor,
23
+ required = false,
24
+ errorSpacing,
25
+ loading = false,
26
+ leadingIcon,
27
+ leadingIconColor,
28
+ style,
29
+ ...props
30
+ }: InputDropDownProps) => {
31
+ const {theme} = useContext(ApplicationContext);
32
+ const full = !!value || !!placeholder;
33
+
34
+ const renderIcon = (color: string | undefined) => {
35
+ if (loading) {
36
+ return <Loader type={'spinner'} color={color} />;
37
+ }
38
+ if (icon) {
39
+ return <Icon color={color} source={icon} size={24} />;
40
+ }
41
+ };
42
+
43
+ const renderInputView = () => {
44
+ const disabledColor = theme.colors.text.disable;
45
+ let textColor = theme.colors.text.default;
46
+ let placeholderColor = theme.colors.text.hint;
47
+ let iconTintColor = iconColor;
48
+
49
+ if (disabled) {
50
+ textColor = disabledColor;
51
+ placeholderColor = disabledColor;
52
+ iconTintColor = disabledColor;
53
+ }
54
+
55
+ return (
56
+ <View
57
+ style={[
58
+ styles.inputWrapper,
59
+ {backgroundColor: theme.colors.background.surface},
60
+ getSizeStyle(size),
61
+ getBorderColor(theme, false, errorMessage, disabled),
62
+ ]}>
63
+ <FloatingView
64
+ floatingValue={floatingValue}
65
+ floatingIconColor={floatingIconColor}
66
+ disabled={disabled}
67
+ required={required}
68
+ floatingIcon={floatingIcon}
69
+ />
70
+ <View style={full ? styles.inputView : Styles.row}>
71
+ {!!leadingIcon && (
72
+ <View style={styles.leadingIconContainer}>
73
+ <Icon
74
+ color={leadingIconColor}
75
+ source={leadingIcon}
76
+ size={size === 'small' ? 24 : 32}
77
+ />
78
+ </View>
79
+ )}
80
+ {full && (
81
+ <TextInput
82
+ {...props}
83
+ onPressIn={onPress}
84
+ editable={false}
85
+ textAlignVertical="top"
86
+ style={[
87
+ styles.input,
88
+ {
89
+ color: textColor,
90
+ fontFamily: `${theme.font}-Regular`,
91
+ },
92
+ ]}
93
+ textBreakStrategy="highQuality"
94
+ value={value}
95
+ placeholder={placeholder}
96
+ selectionColor={theme.colors.primary}
97
+ placeholderTextColor={placeholderColor}
98
+ />
99
+ )}
100
+ </View>
101
+ <View style={styles.iconView}>{renderIcon(iconTintColor)}</View>
102
+ </View>
103
+ );
104
+ };
105
+
106
+ return (
107
+ <TouchableOpacity onPress={onPress} style={[style, full && styles.wrapper]}>
108
+ {renderInputView()}
109
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
110
+ </TouchableOpacity>
111
+ );
112
+ };
113
+
114
+ export default InputDropDown;
@@ -0,0 +1,163 @@
1
+ import React, {
2
+ forwardRef,
3
+ useContext,
4
+ useImperativeHandle,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import {
9
+ NativeSyntheticEvent,
10
+ TextInput,
11
+ TextInputFocusEventData,
12
+ TouchableOpacity,
13
+ View,
14
+ } from 'react-native';
15
+ import {ApplicationContext} from '../Application';
16
+ import styles from './styles';
17
+ import {ErrorView, FloatingView, getBorderColor, getSizeStyle} from './common';
18
+ import {InputMoneyProps} from './index';
19
+ import {Icon} from '../Icon';
20
+ import {formatMoneyToNumber, formatNumberToMoney} from './utils';
21
+
22
+ const InputMoney = forwardRef(
23
+ (
24
+ {
25
+ onChangeText,
26
+ floatingValue,
27
+ floatingIcon,
28
+ size = 'small',
29
+ onBlur,
30
+ onFocus,
31
+ errorMessage,
32
+ icon,
33
+ disabled = false,
34
+ floatingIconColor,
35
+ iconColor,
36
+ required = false,
37
+ errorSpacing,
38
+ style,
39
+ defaultValue = '',
40
+ ...props
41
+ }: InputMoneyProps,
42
+ ref,
43
+ ) => {
44
+ const {theme} = useContext(ApplicationContext);
45
+ const [focused, setFocused] = useState(false);
46
+ const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
47
+
48
+ const validateText = (text: string) => {
49
+ const valueFormat = formatMoneyToNumber(text, 'đ');
50
+ return formatNumberToMoney(valueFormat, 'đ');
51
+ };
52
+
53
+ const [value, setValue] = useState(
54
+ defaultValue ? validateText(defaultValue) : '',
55
+ );
56
+
57
+ const onClearText = () => {
58
+ inputRef?.current?.clear();
59
+ setValue('');
60
+ onChangeText?.('');
61
+ };
62
+
63
+ const _onChangeText = (text: string) => {
64
+ if (text.length < value.length && value.indexOf(text) === 0) {
65
+ text = value.slice(0, -2) + 'đ';
66
+ }
67
+ setValue(validateText(text));
68
+ onChangeText?.(formatMoneyToNumber(text, 'đ').toString());
69
+ };
70
+
71
+ useImperativeHandle(ref, () => {
72
+ return {
73
+ setText: _onChangeText,
74
+ };
75
+ });
76
+
77
+ const _onFocus = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
78
+ setFocused(true);
79
+ onFocus?.(e);
80
+ };
81
+
82
+ const _onBlur = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
83
+ setFocused(false);
84
+ onBlur?.(e);
85
+ };
86
+
87
+ const renderInputView = () => {
88
+ const disabledColor = theme.colors.text.disable;
89
+ let textColor = theme.colors.text.default;
90
+ let placeholderColor = theme.colors.text.hint;
91
+ let iconTintColor = iconColor;
92
+
93
+ if (disabled) {
94
+ textColor = disabledColor;
95
+ placeholderColor = disabledColor;
96
+ iconTintColor = disabledColor;
97
+ }
98
+
99
+ return (
100
+ <View
101
+ style={[
102
+ styles.inputWrapper,
103
+ {backgroundColor: theme.colors.background.surface},
104
+ getSizeStyle(size),
105
+ getBorderColor(theme, focused, errorMessage, disabled),
106
+ ]}>
107
+ <FloatingView
108
+ floatingValue={floatingValue}
109
+ floatingIconColor={floatingIconColor}
110
+ disabled={disabled}
111
+ required={required}
112
+ floatingIcon={floatingIcon}
113
+ />
114
+ <View style={styles.inputView}>
115
+ <TextInput
116
+ {...props}
117
+ editable={!disabled}
118
+ textAlignVertical="top"
119
+ ref={inputRef}
120
+ keyboardType={'number-pad'}
121
+ style={[
122
+ styles.moneyInput,
123
+ {
124
+ color: textColor,
125
+ },
126
+ ]}
127
+ value={value}
128
+ onChangeText={_onChangeText}
129
+ onFocus={_onFocus}
130
+ onBlur={_onBlur}
131
+ selectionColor={theme.colors.primary}
132
+ placeholderTextColor={placeholderColor}
133
+ placeholder={'0đ'}
134
+ />
135
+ </View>
136
+ <View style={styles.iconView}>
137
+ {focused && (
138
+ <TouchableOpacity
139
+ style={styles.iconWrapper}
140
+ onPress={onClearText}>
141
+ <Icon
142
+ source="24_navigation_close_circle_full"
143
+ size={16}
144
+ color={theme.colors.text.hint}
145
+ />
146
+ </TouchableOpacity>
147
+ )}
148
+ {!!icon && <Icon color={iconTintColor} source={icon} />}
149
+ </View>
150
+ </View>
151
+ );
152
+ };
153
+
154
+ return (
155
+ <View style={[style, styles.wrapper]}>
156
+ {renderInputView()}
157
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
158
+ </View>
159
+ );
160
+ },
161
+ );
162
+
163
+ export default InputMoney;
@@ -0,0 +1,235 @@
1
+ import React, {
2
+ FC,
3
+ forwardRef,
4
+ useContext,
5
+ useEffect,
6
+ useImperativeHandle,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import {
11
+ Animated,
12
+ NativeSyntheticEvent,
13
+ TextInput,
14
+ TextInputFocusEventData,
15
+ TouchableOpacity,
16
+ View,
17
+ } from 'react-native';
18
+ import {CaretProps, InputOTPProps} from './index';
19
+ import {scaleSize, Text} from '../Text';
20
+ import styles from './styles';
21
+ import {ErrorView, getBorderColor} from './common';
22
+ import {ApplicationContext} from '../Application';
23
+ import {Spacing} from '../Consts';
24
+
25
+ const OTPCaret: FC<CaretProps> = ({index, length}) => {
26
+ const DURATION = 300;
27
+ const {theme} = useContext(ApplicationContext);
28
+ const opacity = useRef(new Animated.Value(0)).current;
29
+
30
+ useEffect(() => {
31
+ Animated.loop(
32
+ Animated.sequence([
33
+ Animated.timing(opacity, {
34
+ toValue: 1,
35
+ duration: DURATION,
36
+ useNativeDriver: true,
37
+ }),
38
+ Animated.delay(DURATION * 2),
39
+ Animated.timing(opacity, {
40
+ toValue: 0,
41
+ duration: DURATION,
42
+ useNativeDriver: true,
43
+ }),
44
+ ]),
45
+ ).start();
46
+ }, []);
47
+ const spacingStyle = !isNaN(Number(length)) &&
48
+ index !== Number(length) - 1 && {marginRight: Spacing.L};
49
+
50
+ return (
51
+ <View style={[{flexDirection: 'row', alignItems: 'center'}, spacingStyle]}>
52
+ <Animated.View
53
+ style={{
54
+ height: scaleSize(12),
55
+ width: 1,
56
+ backgroundColor: theme.colors.primary,
57
+ opacity,
58
+ }}
59
+ />
60
+ <Text color={theme.colors.text.hint} typography={'body_default_regular'}>
61
+ -
62
+ </Text>
63
+ </View>
64
+ );
65
+ };
66
+
67
+ const InputOTP = forwardRef(
68
+ (
69
+ {
70
+ length,
71
+ errorMessage,
72
+ errorSpacing,
73
+ floatingValue,
74
+ floatingIconColor,
75
+ floatingIcon,
76
+ placeholder,
77
+ onChangeText,
78
+ onFocus,
79
+ onBlur,
80
+ dataType = 'number',
81
+ style,
82
+ ...props
83
+ }: InputOTPProps,
84
+ ref,
85
+ ) => {
86
+ const MAX_LENGTH = 10;
87
+ const [value, setValue] = useState('');
88
+ const [focused, setFocused] = useState(false);
89
+ const inputRef = useRef<TextInput>(null);
90
+ const {theme} = useContext(ApplicationContext);
91
+
92
+ useImperativeHandle(ref, () => ({
93
+ onChangeText: (text: string) => {
94
+ _onChangeText(text);
95
+ },
96
+ }));
97
+
98
+ const onFocusInput = () => {
99
+ setFocused(true);
100
+ inputRef.current?.focus();
101
+ };
102
+
103
+ const onBlurInput = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
104
+ setFocused(false);
105
+ onBlur?.(e);
106
+ };
107
+
108
+ const _onChangeText = (text: string) => {
109
+ if (text.length > MAX_LENGTH) return;
110
+ if (dataType === 'number' && isNaN(Number(text))) return;
111
+ if (
112
+ !isNaN(Number(text)) &&
113
+ value.length >= Number(length) &&
114
+ text.length >= value.length
115
+ )
116
+ return;
117
+
118
+ setValue(text);
119
+ onChangeText?.(text);
120
+ };
121
+
122
+ const renderInputs = (length: number) => {
123
+ const TextInputs: React.ReactNode[] = [];
124
+ for (let i = 0; i < length; i++) {
125
+ TextInputs.push(
126
+ <View>
127
+ {focused && value.length === i ? (
128
+ <OTPCaret index={i} length={length} />
129
+ ) : (
130
+ <Text
131
+ style={i !== length - 1 && {marginRight: Spacing.L}}
132
+ color={
133
+ !!value[i]
134
+ ? theme.colors.text.default
135
+ : theme.colors.text.hint
136
+ }
137
+ typography={
138
+ !!value[i] ? 'header_m_bold' : 'body_default_regular'
139
+ }>
140
+ {value[i] || '-'}
141
+ </Text>
142
+ )}
143
+ </View>,
144
+ );
145
+ }
146
+ return TextInputs;
147
+ };
148
+
149
+ const renderUnidentifiedInputs = () => {
150
+ const splitValue = value.split('');
151
+
152
+ if (!focused && value.length === 0 && !!placeholder) {
153
+ return (
154
+ <Text
155
+ color={theme.colors.text.hint}
156
+ typography={'description_default_regular'}>
157
+ {placeholder}
158
+ </Text>
159
+ );
160
+ }
161
+
162
+ return (
163
+ <View style={{flexDirection: 'row'}}>
164
+ {splitValue.map((letter, index) => {
165
+ return (
166
+ <Text
167
+ style={
168
+ (index !== value.length - 1 ||
169
+ (focused && value.length !== MAX_LENGTH)) && {
170
+ marginRight: Spacing.L,
171
+ }
172
+ }
173
+ typography={'header_m_bold'}>
174
+ {letter}
175
+ </Text>
176
+ );
177
+ })}
178
+ {focused && value.length !== MAX_LENGTH && <OTPCaret />}
179
+ </View>
180
+ );
181
+ };
182
+
183
+ const renderInputView = () => {
184
+ return (
185
+ <View
186
+ style={[
187
+ styles.otpInput,
188
+ {backgroundColor: theme.colors.background.surface},
189
+ getBorderColor(theme, focused, errorMessage),
190
+ ]}>
191
+ {!!floatingValue && (
192
+ <View
193
+ style={[
194
+ styles.otpFloatingView,
195
+ {backgroundColor: theme.colors.background.surface},
196
+ ]}>
197
+ <Text typography={'description_default_regular'}>
198
+ {floatingValue}
199
+ </Text>
200
+ </View>
201
+ )}
202
+ <View style={styles.otpInputsView}>
203
+ {length ? renderInputs(length) : renderUnidentifiedInputs()}
204
+ </View>
205
+ <View style={styles.inputView}>
206
+ <TextInput
207
+ {...props}
208
+ ref={inputRef}
209
+ value={value}
210
+ onChangeText={_onChangeText}
211
+ keyboardType={dataType === 'number' ? 'number-pad' : 'default'}
212
+ onFocus={onFocusInput}
213
+ onBlur={onBlurInput}
214
+ style={{opacity: 0}}
215
+ selectionColor={theme.colors.primary}
216
+ placeholderTextColor={theme.colors.text.hint}
217
+ />
218
+ </View>
219
+ </View>
220
+ );
221
+ };
222
+
223
+ return (
224
+ <TouchableOpacity
225
+ activeOpacity={1}
226
+ onPress={onFocusInput}
227
+ style={[style, styles.otpWrapper]}>
228
+ {renderInputView()}
229
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
230
+ </TouchableOpacity>
231
+ );
232
+ },
233
+ );
234
+
235
+ export default InputOTP;