@momo-kits/foundation 0.79.6-beta.7 → 0.80.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.
@@ -3,11 +3,13 @@ import {
3
3
  Alert,
4
4
  Animated,
5
5
  DeviceEventEmitter,
6
+ Platform,
6
7
  StatusBar,
7
8
  StyleSheet,
8
- TouchableOpacity,
9
9
  View,
10
+ TouchableOpacity as TouchableRN,
10
11
  } from 'react-native';
12
+ import {TouchableOpacity as TouchableGesture} from 'react-native-gesture-handler';
11
13
  import {ApplicationContext} from './index';
12
14
  import {Colors, Radius, Spacing, Styles} from '../Consts';
13
15
  import {Image} from '../Image';
@@ -22,6 +24,8 @@ import {scaleSize, Text} from '../Text';
22
24
  import {Icon} from '../Icon';
23
25
  import {PopupNotify} from '../Popup';
24
26
 
27
+ const Touchable = Platform.OS === 'android' ? TouchableGesture : TouchableRN;
28
+
25
29
  const styles = StyleSheet.create({
26
30
  navigationButton: {
27
31
  height: 28,
@@ -101,7 +105,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
101
105
  }
102
106
 
103
107
  return (
104
- <TouchableOpacity
108
+ <Touchable
105
109
  accessibilityLabel={`NavigationButton/${accessibilityLabel}`}
106
110
  style={[
107
111
  styles.navigationButton,
@@ -116,7 +120,7 @@ const NavigationButton: React.FC<NavigationButtonProps> = ({
116
120
  color={tintColor ?? theme.colors.text.default}
117
121
  size={20}
118
122
  />
119
- </TouchableOpacity>
123
+ </Touchable>
120
124
  );
121
125
  };
122
126
 
@@ -392,21 +396,17 @@ const HeaderToolkitAction: React.FC<any> = ({tintColor}) => {
392
396
  borderWidth: backgroundColor ? 0.5 : 0,
393
397
  },
394
398
  ]}>
395
- <TouchableOpacity
396
- accessibilityLabel={'Toolkit/More'}
397
- onPress={onMore}>
399
+ <Touchable accessibilityLabel={'Toolkit/More'} onPress={onMore}>
398
400
  <Icon color={tintColor} source="navigation_more_horiz" size={20} />
399
- </TouchableOpacity>
401
+ </Touchable>
400
402
  <View style={[styles.divider, {backgroundColor: tintColor}]} />
401
- <TouchableOpacity
402
- accessibilityLabel={`Toolkit/Close`}
403
- onPress={onClose}>
403
+ <Touchable accessibilityLabel={`Toolkit/Close`} onPress={onClose}>
404
404
  <Icon
405
405
  color={tintColor}
406
406
  source="16_navigation_close_circle"
407
407
  size={20}
408
408
  />
409
- </TouchableOpacity>
409
+ </Touchable>
410
410
  </View>
411
411
  </View>
412
412
  );
@@ -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
  },
@@ -42,6 +42,9 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
42
42
  }, []);
43
43
 
44
44
  useEffect(() => {
45
+ maxApi?.getLanguage?.((lang: 'vi' | 'en') => {
46
+ setLanguage(lang);
47
+ });
45
48
  const listener = maxApi?.observer?.('language', (lang: 'vi' | 'en') => {
46
49
  setLanguage(lang);
47
50
  });
@@ -1,4 +1,4 @@
1
- import React, {useContext, useEffect} from 'react';
1
+ import React, {useContext, useLayoutEffect} from 'react';
2
2
  import {ScreenParams} from './types';
3
3
  import Navigation from './Navigation';
4
4
  import {ApplicationContext} from './index';
@@ -16,7 +16,7 @@ const StackScreen: React.FC<ScreenParams> = props => {
16
16
 
17
17
  delete params.screen;
18
18
 
19
- useEffect(() => {
19
+ useLayoutEffect(() => {
20
20
  if (options) {
21
21
  navigation.setOptions(options);
22
22
  }
package/Input/Input.tsx CHANGED
@@ -13,7 +13,6 @@ import {InputProps} from './index';
13
13
  import {Icon} from '../Icon';
14
14
  import {checkTyping} from './utils';
15
15
  import {Loader} from '../Loader';
16
- import {Image} from '../Image';
17
16
 
18
17
  const Input = forwardRef(
19
18
  (
@@ -34,7 +33,8 @@ const Input = forwardRef(
34
33
  required,
35
34
  errorSpacing,
36
35
  loading,
37
- image,
36
+ leadingIcon,
37
+ leadingIconColor,
38
38
  ...props
39
39
  }: InputProps,
40
40
  ref,
@@ -78,7 +78,6 @@ const Input = forwardRef(
78
78
  let textColor = theme.colors.text.default;
79
79
  let placeholderColor = theme.colors.text.hint;
80
80
  let iconTintColor = iconColor;
81
- const imageStyle = size === 'small' ? styles.imageSmall : styles.image;
82
81
 
83
82
  if (disabled) {
84
83
  textColor = disabledColor;
@@ -102,7 +101,15 @@ const Input = forwardRef(
102
101
  floatingIcon={floatingIcon}
103
102
  />
104
103
  <View style={styles.inputView}>
105
- {!!image && <Image style={imageStyle} source={{uri: image}} />}
104
+ {!!leadingIcon && (
105
+ <View style={styles.leadingIconContainer}>
106
+ <Icon
107
+ color={leadingIconColor}
108
+ source={leadingIcon}
109
+ size={size === 'small' ? 24 : 32}
110
+ />
111
+ </View>
112
+ )}
106
113
  <TextInput
107
114
  {...props}
108
115
  accessibilityLabel={`TextInput`}
@@ -0,0 +1,127 @@
1
+ import React, {forwardRef, useContext, useRef} 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 = forwardRef(
12
+ (
13
+ {
14
+ value,
15
+ floatingValue,
16
+ floatingIcon,
17
+ size,
18
+ onPress,
19
+ placeholder,
20
+ errorMessage,
21
+ icon = 'arrow_chevron_down_small',
22
+ disabled,
23
+ floatingIconColor,
24
+ iconColor,
25
+ required,
26
+ errorSpacing,
27
+ loading,
28
+ leadingIcon,
29
+ leadingIconColor,
30
+ ...props
31
+ }: InputDropDownProps,
32
+ ref,
33
+ ) => {
34
+ const {theme} = useContext(ApplicationContext);
35
+ const inputRef = (ref as React.RefObject<TextInput>) ?? useRef<TextInput>();
36
+ const full = !!value || !!placeholder;
37
+ const renderIcon = (color: string | undefined) => {
38
+ if (loading) {
39
+ return <Loader type={'spinner'} color={color} />;
40
+ }
41
+ if (icon) {
42
+ return <Icon color={color} source={icon} size={24} />;
43
+ }
44
+ };
45
+
46
+ const renderInputView = () => {
47
+ const disabledColor = theme.colors.text.disable;
48
+ let textColor = theme.colors.text.default;
49
+ let placeholderColor = theme.colors.text.hint;
50
+ let iconTintColor = iconColor;
51
+
52
+ if (disabled) {
53
+ textColor = disabledColor;
54
+ placeholderColor = disabledColor;
55
+ iconTintColor = disabledColor;
56
+ }
57
+
58
+ return (
59
+ <View
60
+ style={[
61
+ styles.inputWrapper,
62
+ {backgroundColor: theme.colors.background.surface},
63
+ getSizeStyle(size),
64
+ getBorderColor(theme, false, errorMessage, disabled),
65
+ ]}>
66
+ <FloatingView
67
+ floatingValue={floatingValue}
68
+ floatingIconColor={floatingIconColor}
69
+ disabled={disabled}
70
+ required={required}
71
+ floatingIcon={floatingIcon}
72
+ />
73
+ <View style={full ? styles.inputView : Styles.row}>
74
+ {!!leadingIcon && (
75
+ <View style={styles.leadingIconContainer}>
76
+ <Icon
77
+ color={leadingIconColor}
78
+ source={leadingIcon}
79
+ size={size === 'small' ? 24 : 32}
80
+ />
81
+ </View>
82
+ )}
83
+ {full && (
84
+ <TextInput
85
+ {...props}
86
+ onPressIn={onPress}
87
+ accessibilityLabel={`TextInput`}
88
+ editable={false}
89
+ textAlignVertical="top"
90
+ ref={inputRef}
91
+ style={[
92
+ styles.input,
93
+ {
94
+ color: textColor,
95
+ fontFamily: `${theme.font}-Regular`,
96
+ },
97
+ ]}
98
+ textBreakStrategy="highQuality"
99
+ value={value}
100
+ placeholder={placeholder}
101
+ selectionColor={theme.colors.primary}
102
+ placeholderTextColor={placeholderColor}
103
+ />
104
+ )}
105
+ </View>
106
+ <View style={styles.iconView}>{renderIcon(iconTintColor)}</View>
107
+ </View>
108
+ );
109
+ };
110
+
111
+ return (
112
+ <TouchableOpacity
113
+ onPress={onPress}
114
+ style={full && styles.wrapper}
115
+ accessibilityLabel={`InputDropDown/${floatingValue}`}>
116
+ {renderInputView()}
117
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
118
+ </TouchableOpacity>
119
+ );
120
+ },
121
+ );
122
+
123
+ InputDropDown.defaultProps = {
124
+ size: 'small',
125
+ };
126
+
127
+ export default InputDropDown;
@@ -0,0 +1,213 @@
1
+ import React, {FC, useContext, useEffect, useRef, useState} from 'react';
2
+ import {
3
+ Animated,
4
+ KeyboardType,
5
+ NativeSyntheticEvent,
6
+ TextInput,
7
+ TextInputFocusEventData,
8
+ TouchableOpacity,
9
+ View,
10
+ } from 'react-native';
11
+ import {CaretProps, InputOTPProps} from './index';
12
+ import {scaleSize, Text} from '../Text';
13
+ import styles from './styles';
14
+ import {ErrorView, getBorderColor} from './common';
15
+ import {ApplicationContext} from '../Application';
16
+ import {Spacing} from '../Consts';
17
+
18
+ const OTPCaret: FC<CaretProps> = ({index, length}) => {
19
+ const DURATION = 300;
20
+ const {theme} = useContext(ApplicationContext);
21
+ const opacity = useRef(new Animated.Value(0)).current;
22
+
23
+ useEffect(() => {
24
+ Animated.loop(
25
+ Animated.sequence([
26
+ Animated.timing(opacity, {
27
+ toValue: 1,
28
+ duration: DURATION,
29
+ useNativeDriver: true,
30
+ }),
31
+ Animated.delay(DURATION * 2),
32
+ Animated.timing(opacity, {
33
+ toValue: 0,
34
+ duration: DURATION,
35
+ useNativeDriver: true,
36
+ }),
37
+ ]),
38
+ ).start();
39
+ }, []);
40
+ const spacingStyle = !isNaN(Number(length)) &&
41
+ index !== Number(length) - 1 && {marginRight: Spacing.L};
42
+
43
+ return (
44
+ <View style={[{flexDirection: 'row', alignItems: 'center'}, spacingStyle]}>
45
+ <Animated.View
46
+ style={{
47
+ height: scaleSize(12),
48
+ width: 1,
49
+ backgroundColor: theme.colors.primary,
50
+ opacity,
51
+ }}
52
+ />
53
+ <Text color={theme.colors.text.hint} typography={'paragraph_default'}>
54
+ -
55
+ </Text>
56
+ </View>
57
+ );
58
+ };
59
+
60
+ const InputOTP: FC<InputOTPProps> = ({
61
+ length,
62
+ errorMessage,
63
+ errorSpacing,
64
+ floatingValue,
65
+ floatingIconColor,
66
+ floatingIcon,
67
+ placeholder,
68
+ onChangeText,
69
+ onFocus,
70
+ onBlur,
71
+ dataType = 'number',
72
+ ...props
73
+ }) => {
74
+ const MAX_LENGTH = 10;
75
+ const [value, setValue] = useState('');
76
+ const [focused, setFocused] = useState(false);
77
+ const inputRef = useRef<TextInput>(null);
78
+ const {theme} = useContext(ApplicationContext);
79
+
80
+ const onFocusInput = () => {
81
+ setFocused(true);
82
+ inputRef.current?.focus();
83
+ };
84
+
85
+ const onBlurInput = (e: NativeSyntheticEvent<TextInputFocusEventData>) => {
86
+ setFocused(false);
87
+ onBlur?.(e);
88
+ };
89
+
90
+ const _onChangeText = (text: string) => {
91
+ if (text.length > MAX_LENGTH) return;
92
+ if (dataType === 'number' && isNaN(Number(text))) return;
93
+ if (
94
+ !isNaN(Number(text)) &&
95
+ value.length >= Number(length) &&
96
+ text.length >= value.length
97
+ )
98
+ return;
99
+
100
+ setValue(text);
101
+ onChangeText?.(text);
102
+ };
103
+
104
+ const renderInputs = (length: number) => {
105
+ const TextInputs = [];
106
+ for (let i = 0; i < length; i++) {
107
+ TextInputs.push(
108
+ <View>
109
+ {focused && value.length === i ? (
110
+ <OTPCaret index={i} length={length} />
111
+ ) : (
112
+ <Text
113
+ style={i !== length - 1 && {marginRight: Spacing.L}}
114
+ color={
115
+ !!value[i] ? theme.colors.text.default : theme.colors.text.hint
116
+ }
117
+ typography={!!value[i] ? 'title_s' : 'paragraph_default'}>
118
+ {value[i] || '-'}
119
+ </Text>
120
+ )}
121
+ </View>,
122
+ );
123
+ }
124
+ return TextInputs;
125
+ };
126
+
127
+ const renderUnidentifiedInputs = () => {
128
+ const splitValue = value.split('');
129
+
130
+ if (!focused && value.length === 0 && !!placeholder) {
131
+ return (
132
+ <Text color={theme.colors.text.hint} typography={'description_s'}>
133
+ {placeholder}
134
+ </Text>
135
+ );
136
+ }
137
+
138
+ return (
139
+ <View style={{flexDirection: 'row'}}>
140
+ {splitValue.map((letter, index) => {
141
+ return (
142
+ <Text
143
+ style={
144
+ (index !== value.length - 1 ||
145
+ (focused && value.length !== MAX_LENGTH)) && {
146
+ marginRight: Spacing.L,
147
+ }
148
+ }
149
+ typography={'title_s'}>
150
+ {letter}
151
+ </Text>
152
+ );
153
+ })}
154
+ {focused && value.length !== MAX_LENGTH && <OTPCaret />}
155
+ </View>
156
+ );
157
+ };
158
+
159
+ const renderInputView = () => {
160
+ let placeholderColor = theme.colors.text.hint;
161
+ const keyboardType: KeyboardType =
162
+ dataType === 'number' ? 'number-pad' : 'default';
163
+ return (
164
+ <View
165
+ style={[
166
+ styles.otpInput,
167
+ {backgroundColor: theme.colors.background.surface},
168
+ getBorderColor(theme, focused, errorMessage),
169
+ ]}>
170
+ {!!floatingValue && (
171
+ <View
172
+ style={[
173
+ styles.otpFloatingView,
174
+ {backgroundColor: theme.colors.background.surface},
175
+ ]}>
176
+ <Text typography={'description_s'}>{floatingValue}</Text>
177
+ </View>
178
+ )}
179
+ <View style={styles.otpInputsView}>
180
+ {length ? renderInputs(length) : renderUnidentifiedInputs()}
181
+ </View>
182
+ <View style={styles.inputView}>
183
+ <TextInput
184
+ {...props}
185
+ accessibilityLabel={`TextInput`}
186
+ ref={inputRef}
187
+ value={value}
188
+ onChangeText={_onChangeText}
189
+ keyboardType={keyboardType}
190
+ onFocus={onFocusInput}
191
+ onBlur={onBlurInput}
192
+ style={{opacity: 0}}
193
+ selectionColor={theme.colors.primary}
194
+ placeholderTextColor={placeholderColor}
195
+ />
196
+ </View>
197
+ </View>
198
+ );
199
+ };
200
+
201
+ return (
202
+ <TouchableOpacity
203
+ activeOpacity={1}
204
+ onPress={onFocusInput}
205
+ style={styles.otpWrapper}
206
+ accessibilityLabel={`Input/${floatingValue}`}>
207
+ {renderInputView()}
208
+ <ErrorView errorMessage={errorMessage} errorSpacing={errorSpacing} />
209
+ </TouchableOpacity>
210
+ );
211
+ };
212
+
213
+ export default InputOTP;
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,8 @@ 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';
6
+ import InputDropDown from './InputDropDown';
5
7
  import {TextInputProps} from 'react-native';
6
8
 
7
9
  export interface InputProps extends TextInputProps {
@@ -30,6 +32,11 @@ export interface InputProps extends TextInputProps {
30
32
  */
31
33
  icon?: string;
32
34
 
35
+ /**
36
+ * Optional. Represents the color of the icon in the Input component.
37
+ */
38
+ iconColor?: string;
39
+
33
40
  /**
34
41
  * Optional. If `true`, the user won't be able to interact with the Input component.
35
42
  * Defaults to `false` if not provided.
@@ -41,11 +48,6 @@ export interface InputProps extends TextInputProps {
41
48
  */
42
49
  floatingIconColor?: string;
43
50
 
44
- /**
45
- * Optional. Represents the color of the icon in the Input component.
46
- */
47
- iconColor?: string;
48
-
49
51
  /**
50
52
  * Optional. If `true`, the Input component is marked as required,
51
53
  * indicating that the user must provide a value before submitting a form.
@@ -65,13 +67,22 @@ export interface InputProps extends TextInputProps {
65
67
  */
66
68
  loading?: boolean;
67
69
 
68
- image?: string;
70
+ /**
71
+ * Optional. Represents the leading icon in the Input component.
72
+ */
73
+ leadingIcon?: string;
74
+
75
+ /**
76
+ * Optional. Represents the color of the leading icon in the Input component.
77
+ */
78
+ leadingIconColor?: string;
69
79
  }
70
80
 
71
81
  type InputPropsWithoutSizeAndIcon = Omit<
72
82
  InputProps,
73
83
  'size' | 'icon' | 'iconColor'
74
84
  >;
85
+
75
86
  type InputPropsOmitForSearch = Omit<
76
87
  InputProps,
77
88
  'required' | 'size' | 'errorMessage' | 'disabled'
@@ -109,4 +120,102 @@ export interface InputSearchProps extends InputPropsOmitForSearch {
109
120
 
110
121
  export interface InputMoneyProps extends InputPropsWithoutPlaceholder {}
111
122
 
112
- export {Input, InputTextArea, InputSearch, InputMoney};
123
+ export type OTPInputLength = 2 | 4 | 6 | 8 | 10;
124
+
125
+ export interface InputOTPProps extends InputPropsWithoutSizeAndIcon {
126
+ length?: OTPInputLength;
127
+ dataType?: 'string' | 'number';
128
+ }
129
+
130
+ export type CaretProps = {
131
+ index?: number;
132
+ length?: number;
133
+ };
134
+
135
+ export type InputDropDownProps = {
136
+ /**
137
+ * Optional. Defines the size of the Input component.
138
+ */
139
+ size?: 'small' | 'large';
140
+
141
+ /**
142
+ * Optional. Defines the value of InputDropDown.
143
+ */
144
+ value?: string;
145
+
146
+ /**
147
+ * Optional. Defines the placeholder of InputDropDown.
148
+ */
149
+ placeholder?: string;
150
+
151
+ /**
152
+ * Optional. Defines the onPress of InputDropDown.
153
+ */
154
+ onPress?: () => void;
155
+ /**
156
+ * Optional. Represents the value for the floating title in the Input component.
157
+ */
158
+ floatingValue?: string;
159
+
160
+ /**
161
+ * Optional. Represents the name or key of the floating icon to be displayed in the Input component.
162
+ */
163
+ floatingIcon?: string;
164
+
165
+ /**
166
+ * Optional. Represents the error message to be displayed below the Input component when there is an error.
167
+ */
168
+ errorMessage?: string;
169
+
170
+ /**
171
+ * Optional. Represents the name or key of the icon to be displayed in the Input component.
172
+ */
173
+ icon?: string;
174
+
175
+ /**
176
+ * Optional. Represents the color of the icon in the Input component.
177
+ */
178
+ iconColor?: string;
179
+
180
+ /**
181
+ * Optional. If `true`, the user won't be able to interact with the Input component.
182
+ * Defaults to `false` if not provided.
183
+ */
184
+ disabled?: boolean;
185
+
186
+ /**
187
+ * Optional. Represents the color of the floating icon in the Input component.
188
+ */
189
+ floatingIconColor?: string;
190
+
191
+ /**
192
+ * Optional. If `true`, the Input component is marked as required,
193
+ * indicating that the user must provide a value before submitting a form.
194
+ * Defaults to `false` if not provided.
195
+ */
196
+ required?: boolean;
197
+
198
+ /**
199
+ * Optional. If `true`,
200
+ * include spacing when Input not have error message.
201
+ * Defaults to `false` if not provided.
202
+ */
203
+ errorSpacing?: boolean;
204
+
205
+ /**
206
+ * If true, the icon of input will use be show indicator loading.
207
+ */
208
+ loading?: boolean;
209
+
210
+ /**
211
+ * Optional. Represents the leading icon in the Input component.
212
+ */
213
+ leadingIcon?: string;
214
+
215
+ /**
216
+ * Optional. Represents the color of the leading icon in the Input component.
217
+ */
218
+ leadingIconColor?: string;
219
+ };
220
+
221
+ export {Input, InputTextArea, InputSearch, InputMoney, InputOTP, InputDropDown};
package/Input/styles.ts CHANGED
@@ -42,17 +42,10 @@ export default StyleSheet.create({
42
42
  flexDirection: 'row',
43
43
  alignItems: 'center',
44
44
  },
45
- image: {
46
- width: 32,
47
- height: 32,
48
- marginLeft: Spacing.M,
49
- borderRadius: Radius.XS,
50
- },
51
- imageSmall: {
52
- width: 24,
53
- height: 24,
45
+ leadingIconContainer: {
54
46
  marginLeft: Spacing.M,
55
47
  borderRadius: Radius.XS,
48
+ overflow: 'hidden',
56
49
  },
57
50
  iconView: {
58
51
  flexDirection: 'row',
@@ -142,4 +135,28 @@ export default StyleSheet.create({
142
135
  paddingVertical: Spacing.S,
143
136
  fontWeight: 'bold',
144
137
  },
138
+
139
+ //OTP
140
+ otpWrapper: {
141
+ width: '100%',
142
+ marginTop: Spacing.M,
143
+ },
144
+ otpInput: {
145
+ height: scaleSize(56),
146
+ borderRadius: Radius.S,
147
+ borderWidth: 1,
148
+ },
149
+ otpFloatingView: {
150
+ position: 'absolute',
151
+ top: -Spacing.S,
152
+ alignSelf: 'center',
153
+ paddingHorizontal: Spacing.S,
154
+ },
155
+ otpInputsView: {
156
+ width: '100%',
157
+ height: '100%',
158
+ flexDirection: 'row',
159
+ alignItems: 'center',
160
+ justifyContent: 'center',
161
+ },
145
162
  });
package/Layout/Screen.tsx CHANGED
@@ -107,12 +107,10 @@ const Screen: React.FC<ScreenProps> = ({
107
107
  if (useExtendedHeader) {
108
108
  title = {headerTitle: HeaderTitle};
109
109
  }
110
- setImmediate(() => {
111
- navigation?.setOptions({
112
- animatedValue: animatedValue.current,
113
- headerTransparent: headerTransparent,
114
- ...title,
115
- });
110
+ navigation?.setOptions({
111
+ animatedValue: animatedValue.current,
112
+ headerTransparent: headerTransparent,
113
+ ...title,
116
114
  });
117
115
  }
118
116
  }, [useExtendedHeader, useAnimatedHeader]);
@@ -1,4 +1,4 @@
1
- import React, {useContext} from 'react';
1
+ import React, {Fragment, useContext} from 'react';
2
2
  import {Dimensions, View, ViewProps} from 'react-native';
3
3
  import {useGridSystem, validateChildren} from './utils';
4
4
  import {GridContextProps} from './types';
@@ -135,6 +135,9 @@ const Section: React.FC<SectionProps> = ({
135
135
  }
136
136
  } else {
137
137
  element.push(children);
138
+ if (children?.type == Fragment) {
139
+ return renderView(children?.props.children);
140
+ }
138
141
  }
139
142
  return element;
140
143
  };
package/Layout/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {Alert, Dimensions} from 'react-native';
2
- import React, {useEffect, useState} from 'react';
2
+ import React, {Fragment, ReactElement, useEffect, useState} from 'react';
3
3
 
4
4
  /**
5
5
  * validate children type
@@ -7,53 +7,36 @@ import React, {useEffect, useState} from 'react';
7
7
  * @param childrenTypes
8
8
  */
9
9
  const validateChildren = (
10
- children:
11
- | React.ReactElement<any, string | React.JSXElementConstructor<any>>
12
- | string
13
- | number
14
- | {}
15
- | Iterable<React.ReactNode>
16
- | React.ReactPortal
17
- | null
18
- | undefined
19
- | boolean,
20
- childrenTypes: Array<React.FC<any>>,
10
+ children: React.ReactNode | React.ReactNode[],
11
+ childrenTypes: Array<React.FC>,
21
12
  ) => {
22
13
  if (!__DEV__) return children;
23
14
 
24
- let results: React.ReactElement[] = [];
25
15
  const validateType = (child: React.ReactElement) => {
26
- return childrenTypes.some(childrenType => child?.type === childrenType);
27
- };
28
-
29
- const alertWrongType = () => {
30
- Alert.alert(
31
- 'Component Kits',
32
- `Wrong children type for this component, expected are ${childrenTypes
33
- .map(type => type.displayName)
34
- .join(', ')}.`,
16
+ const valid = childrenTypes.some(
17
+ childrenType => child?.type === childrenType || child?.type == Fragment,
35
18
  );
19
+ if (!valid) {
20
+ Alert.alert(
21
+ 'Component Kits',
22
+ `Wrong children type for this component, expected are ${childrenTypes
23
+ .map(type => type.displayName)
24
+ .join(', ')}.`,
25
+ );
26
+ }
27
+ return valid;
36
28
  };
37
29
 
38
30
  if (Array.isArray(children)) {
39
31
  const flattenChildren: React.ReactElement[] = children.flat();
40
- results = flattenChildren.filter((child: React.ReactElement) => {
41
- if (validateType(child)) {
42
- return child;
43
- } else {
44
- alertWrongType();
45
- }
46
- });
47
- } else {
48
- const singleChild: any = children;
49
- if (validateType(singleChild)) {
50
- results = [singleChild];
51
- } else {
52
- alertWrongType();
32
+ for (const child of flattenChildren) {
33
+ validateType(child);
53
34
  }
35
+ return flattenChildren;
36
+ } else {
37
+ validateType(children as ReactElement);
38
+ return children;
54
39
  }
55
-
56
- return results;
57
40
  };
58
41
 
59
42
  const useGridSystem = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.79.6-beta.7",
3
+ "version": "0.80.1",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {
Binary file