@momo-kits/foundation 0.79.6-beta.10 → 0.79.6-beta.12

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.
@@ -241,11 +241,12 @@ const styles = StyleSheet.create({
241
241
  indicatorContainer: {
242
242
  width: '100%',
243
243
  paddingTop: Spacing.S,
244
+ paddingBottom: Spacing.XS,
244
245
  alignItems: 'center',
245
246
  justifyContent: 'center',
246
247
  },
247
248
  indicator: {
248
- width: 64,
249
+ width: 40,
249
250
  height: 4,
250
251
  borderRadius: Radius.S,
251
252
  },
@@ -0,0 +1,171 @@
1
+ import React, {FC, useContext, useRef, useState} from 'react';
2
+ import {
3
+ KeyboardType,
4
+ NativeSyntheticEvent,
5
+ TextInput,
6
+ TextInputFocusEventData,
7
+ TouchableOpacity,
8
+ View,
9
+ } from 'react-native';
10
+ import {InputOTPProps} from './index';
11
+ import {Text} from '../Text';
12
+ import styles from './styles';
13
+ import {ErrorView, getBorderColor} from './common';
14
+ import {ApplicationContext} from '../Application';
15
+ import {Spacing} from '../Consts';
16
+ import OTPCaret from './OTPCaret';
17
+
18
+ const InputOTP: FC<InputOTPProps> = ({
19
+ length,
20
+ errorMessage,
21
+ errorSpacing,
22
+ floatingValue,
23
+ floatingIconColor,
24
+ floatingIcon,
25
+ placeholder,
26
+ onChangeText,
27
+ onFocus,
28
+ onBlur,
29
+ dataType = 'number',
30
+ ...props
31
+ }) => {
32
+ const MAX_LENGTH = 10;
33
+ const [value, setValue] = useState('');
34
+ const [focused, setFocused] = useState(false);
35
+ const inputRef = useRef<TextInput>(null);
36
+ const {theme} = useContext(ApplicationContext);
37
+
38
+ const onFocusInput = () => {
39
+ setFocused(true);
40
+ inputRef.current?.focus();
41
+ };
42
+
43
+ const onBlurInput = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
44
+ setFocused(false);
45
+ onBlur?.(e);
46
+ };
47
+
48
+ const _onChangeText = (text: string) => {
49
+ if (text.length > MAX_LENGTH) return;
50
+ if (dataType === 'number' && isNaN(Number(text))) return;
51
+ if (
52
+ !isNaN(Number(text)) &&
53
+ value.length >= Number(length) &&
54
+ text.length >= value.length
55
+ )
56
+ return;
57
+
58
+ setValue(text);
59
+ onChangeText?.(text);
60
+ };
61
+
62
+ const renderInputs = (length: number) => {
63
+ const TextInputs = [];
64
+ for (let i = 0; i < length; i++) {
65
+ TextInputs.push(
66
+ <View>
67
+ {focused && value.length === i ? (
68
+ <OTPCaret index={i} length={length} />
69
+ ) : (
70
+ <Text
71
+ style={i !== length - 1 && {marginRight: Spacing.L}}
72
+ color={
73
+ !!value[i] ? theme.colors.text.default : theme.colors.text.hint
74
+ }
75
+ typography={!!value[i] ? 'title_s' : 'paragraph_default'}>
76
+ {value[i] || '-'}
77
+ </Text>
78
+ )}
79
+ </View>,
80
+ );
81
+ }
82
+ return TextInputs;
83
+ };
84
+
85
+ const renderUnidentifiedInputs = () => {
86
+ const splitValue = value.split('');
87
+
88
+ if (!focused && value.length === 0 && !!placeholder) {
89
+ return (
90
+ <Text color={theme.colors.text.hint} typography={'description_s'}>
91
+ {placeholder}
92
+ </Text>
93
+ );
94
+ }
95
+
96
+ return (
97
+ <View style={{flexDirection: 'row'}}>
98
+ {splitValue.map((letter, index) => {
99
+ return (
100
+ <Text
101
+ style={
102
+ (index !== value.length - 1 ||
103
+ (focused && value.length !== MAX_LENGTH)) && {
104
+ marginRight: Spacing.L,
105
+ }
106
+ }
107
+ typography={'title_s'}>
108
+ {letter}
109
+ </Text>
110
+ );
111
+ })}
112
+ {focused && value.length !== MAX_LENGTH && <OTPCaret />}
113
+ </View>
114
+ );
115
+ };
116
+
117
+ const renderInputView = () => {
118
+ let placeholderColor = theme.colors.text.hint;
119
+ const keyboardType: KeyboardType =
120
+ dataType === 'number' ? 'number-pad' : 'default';
121
+ return (
122
+ <View
123
+ style={[
124
+ styles.otpInput,
125
+ {backgroundColor: theme.colors.background.surface},
126
+ getBorderColor(theme, focused, errorMessage),
127
+ ]}>
128
+ {!!floatingValue && (
129
+ <View
130
+ style={[
131
+ styles.otpFloatingView,
132
+ {backgroundColor: theme.colors.background.surface},
133
+ ]}>
134
+ <Text typography={'description_s'}>{floatingValue}</Text>
135
+ </View>
136
+ )}
137
+ <View style={styles.otpInputsView}>
138
+ {length ? renderInputs(length) : renderUnidentifiedInputs()}
139
+ </View>
140
+ <View style={styles.inputView}>
141
+ <TextInput
142
+ {...props}
143
+ accessibilityLabel={`TextInput`}
144
+ ref={inputRef}
145
+ value={value}
146
+ onChangeText={_onChangeText}
147
+ keyboardType={keyboardType}
148
+ onFocus={onFocusInput}
149
+ onBlur={onBlurInput}
150
+ style={{opacity: 0}}
151
+ selectionColor={theme.colors.primary}
152
+ placeholderTextColor={placeholderColor}
153
+ />
154
+ </View>
155
+ </View>
156
+ );
157
+ };
158
+
159
+ return (
160
+ <TouchableOpacity
161
+ activeOpacity={1}
162
+ onPress={onFocusInput}
163
+ style={styles.otpWrapper}
164
+ accessibilityLabel={`Input/${floatingValue}`}>
165
+ {renderInputView()}
166
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
167
+ </TouchableOpacity>
168
+ );
169
+ };
170
+
171
+ export default InputOTP;
@@ -0,0 +1,50 @@
1
+ import React, {FC, useContext, useEffect, useRef} from 'react';
2
+ import {Animated, View} from 'react-native';
3
+ import {Spacing} from '../Consts';
4
+ import {scaleSize, Text} from '../Text';
5
+ import {ApplicationContext} from '../Application';
6
+ import {CaretProps} from './index';
7
+
8
+ const OtpCaret: FC<CaretProps> = ({index, length}) => {
9
+ const DURATION = 300;
10
+ const {theme} = useContext(ApplicationContext);
11
+ const opacity = useRef(new Animated.Value(0)).current;
12
+
13
+ useEffect(() => {
14
+ Animated.loop(
15
+ Animated.sequence([
16
+ Animated.timing(opacity, {
17
+ toValue: 1,
18
+ duration: DURATION,
19
+ useNativeDriver: true,
20
+ }),
21
+ Animated.delay(DURATION * 2),
22
+ Animated.timing(opacity, {
23
+ toValue: 0,
24
+ duration: DURATION,
25
+ useNativeDriver: true,
26
+ }),
27
+ ]),
28
+ ).start();
29
+ }, []);
30
+ const spacingStyle = !isNaN(Number(length)) &&
31
+ index !== Number(length) - 1 && {marginRight: Spacing.L};
32
+
33
+ return (
34
+ <View style={[{flexDirection: 'row', alignItems: 'center'}, spacingStyle]}>
35
+ <Animated.View
36
+ style={{
37
+ height: scaleSize(12),
38
+ width: 1,
39
+ backgroundColor: theme.colors.primary,
40
+ opacity,
41
+ }}
42
+ />
43
+ <Text color={theme.colors.text.hint} typography={'paragraph_default'}>
44
+ -
45
+ </Text>
46
+ </View>
47
+ );
48
+ };
49
+
50
+ export default OtpCaret;
package/Input/common.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import {View} from 'react-native';
1
+ import {View, ViewStyle} from 'react-native';
2
2
  import styles from './styles';
3
3
  import {Text} from '../Text';
4
4
  import React, {FC, useContext} from 'react';
@@ -13,6 +13,7 @@ type FloatingViewProps = {
13
13
  disabled?: boolean;
14
14
  floatingIcon?: string;
15
15
  required?: boolean;
16
+ style?: ViewStyle;
16
17
  };
17
18
 
18
19
  export const DEFAULT_HEIGHT = 112;
@@ -81,6 +82,7 @@ export const FloatingView: FC<FloatingViewProps> = ({
81
82
  disabled,
82
83
  floatingIcon,
83
84
  required,
85
+ style,
84
86
  }) => {
85
87
  const {theme} = useContext(ApplicationContext);
86
88
 
@@ -100,6 +102,7 @@ export const FloatingView: FC<FloatingViewProps> = ({
100
102
  backgroundColor: theme.colors.background.surface,
101
103
  zIndex: 10,
102
104
  },
105
+ style,
103
106
  ]}>
104
107
  <Text color={floatingTextColor} typography={'label_s'}>
105
108
  {floatingValue}
package/Input/index.tsx CHANGED
@@ -2,6 +2,7 @@ import Input from './Input';
2
2
  import InputTextArea from './InputTextArea';
3
3
  import InputSearch from './InputSearch';
4
4
  import InputMoney from './InputMoney';
5
+ import InputOTP from './InputOTP';
5
6
  import {TextInputProps} from 'react-native';
6
7
 
7
8
  export interface InputProps extends TextInputProps {
@@ -109,4 +110,15 @@ export interface InputSearchProps extends InputPropsOmitForSearch {
109
110
 
110
111
  export interface InputMoneyProps extends InputPropsWithoutPlaceholder {}
111
112
 
112
- export {Input, InputTextArea, InputSearch, InputMoney};
113
+ export type OTPInputLength = 2 | 4 | 6 | 8 | 10;
114
+
115
+ export interface InputOTPProps extends InputPropsWithoutSizeAndIcon {
116
+ length?: OTPInputLength;
117
+ dataType?: 'string' | 'number';
118
+ }
119
+
120
+ export type CaretProps = {
121
+ index?: number;
122
+ length?: number;
123
+ };
124
+ export {Input, InputTextArea, InputSearch, InputMoney, InputOTP};
package/Input/styles.ts CHANGED
@@ -142,4 +142,28 @@ export default StyleSheet.create({
142
142
  paddingVertical: Spacing.S,
143
143
  fontWeight: 'bold',
144
144
  },
145
+
146
+ //OTP
147
+ otpWrapper: {
148
+ width: '100%',
149
+ marginTop: Spacing.M,
150
+ },
151
+ otpInput: {
152
+ height: scaleSize(56),
153
+ borderRadius: Radius.S,
154
+ borderWidth: 1,
155
+ },
156
+ otpFloatingView: {
157
+ position: 'absolute',
158
+ top: -Spacing.S,
159
+ alignSelf: 'center',
160
+ paddingHorizontal: Spacing.S,
161
+ },
162
+ otpInputsView: {
163
+ width: '100%',
164
+ height: '100%',
165
+ flexDirection: 'row',
166
+ alignItems: 'center',
167
+ justifyContent: 'center',
168
+ },
145
169
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.79.6-beta.10",
3
+ "version": "0.79.6-beta.12",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {