@widergy/mobile-ui 1.11.4 → 1.11.6
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/CHANGELOG.md +14 -0
- package/lib/components/UTButton/index.js +12 -3
- package/lib/components/UTIcon/README.md +22 -8
- package/lib/components/UTIcon/constants.js +24 -0
- package/lib/components/UTIcon/index.js +15 -5
- package/lib/components/UTIcon/theme.js +18 -0
- package/lib/components/UTLabel/README.md +74 -0
- package/lib/components/UTLabel/theme.js +11 -2
- package/lib/components/UTTextArea/constants.js +1 -0
- package/lib/components/UTTextArea/index.js +14 -0
- package/lib/components/UTTextInput/index.js +9 -60
- package/lib/components/UTTextInput/{components → versions/V0/components}/BaseInput/index.js +3 -3
- package/lib/components/UTTextInput/{components → versions/V0/components}/InputAnimatedBorder/index.js +1 -1
- package/lib/components/UTTextInput/{components → versions/V0/components}/InputLabel/index.js +1 -1
- package/lib/components/UTTextInput/{flavors → versions/V0/flavors}/FilledInput/index.js +6 -6
- package/lib/components/UTTextInput/{flavors → versions/V0/flavors}/OutlinedInput/index.js +6 -6
- package/lib/components/UTTextInput/{flavors → versions/V0/flavors}/StandardInput/index.js +6 -6
- package/lib/components/UTTextInput/versions/V0/index.js +67 -0
- package/lib/components/UTTextInput/versions/V1/components/TextInputField/constants.js +12 -0
- package/lib/components/UTTextInput/versions/V1/components/TextInputField/index.js +202 -0
- package/lib/components/UTTextInput/versions/V1/components/TextInputField/theme.js +124 -0
- package/lib/components/UTTextInput/versions/V1/components/TextInputField/utils.js +11 -0
- package/lib/components/UTTextInput/versions/V1/constants.js +8 -0
- package/lib/components/UTTextInput/versions/V1/index.js +94 -0
- package/lib/components/UTTextInput/versions/V1/proptypes.js +36 -0
- package/lib/components/UTTextInput/versions/V1/styles.js +10 -0
- package/lib/components/UTTextInput/versions/V1/utils.js +1 -0
- package/lib/components/UTValidation/README.md +56 -0
- package/lib/components/UTValidation/constants.js +39 -0
- package/lib/components/UTValidation/index.js +69 -0
- package/lib/components/UTValidation/styles.js +25 -0
- package/lib/components/UTValidation/theme.js +17 -0
- package/lib/components/UTValidation/utils.js +8 -0
- package/lib/index.js +5 -2
- package/lib/theming/README.md +10 -18
- package/package.json +1 -1
- package/lib/components/UTTextInput/flavors/FilledInput/constants.js +0 -1
- package/lib/components/UTTextInput/flavors/FilledInput/utils.js +0 -35
- package/lib/components/UTTextInput/flavors/OutlinedInput/constants.js +0 -1
- package/lib/components/UTTextInput/flavors/OutlinedInput/utils.js +0 -37
- package/lib/components/UTTextInput/flavors/StandardInput/constants.js +0 -1
- /package/lib/components/UTTextInput/{components → versions/V0/components}/BaseInput/constants.js +0 -0
- /package/lib/components/UTTextInput/{components → versions/V0/components}/BaseInput/styles.js +0 -0
- /package/lib/components/UTTextInput/{components → versions/V0/components}/InputAnimatedBorder/styles.js +0 -0
- /package/lib/components/UTTextInput/{components → versions/V0/components}/InputAnimatedBorder/utils.js +0 -0
- /package/lib/components/UTTextInput/{components → versions/V0/components}/InputLabel/styles.js +0 -0
- /package/lib/components/UTTextInput/{flavors → versions/V0/flavors}/FilledInput/styles.js +0 -0
- /package/lib/components/UTTextInput/{flavors → versions/V0/flavors}/OutlinedInput/styles.js +0 -0
- /package/lib/components/UTTextInput/{flavors → versions/V0/flavors}/StandardInput/styles.js +0 -0
- /package/lib/components/UTTextInput/{proptypes.js → versions/V0/proptypes.js} +0 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect, useCallback, useLayoutEffect } from 'react';
|
|
2
|
+
import { bool, elementType, func, number, string, shape } from 'prop-types';
|
|
3
|
+
import { View, TextInput } from 'react-native';
|
|
4
|
+
import { ViewPropTypes } from 'deprecated-react-native-prop-types';
|
|
5
|
+
|
|
6
|
+
import { themeType, withTheme } from '../../../../../../theming';
|
|
7
|
+
import UTButton from '../../../../../UTButton';
|
|
8
|
+
import UTIcon from '../../../../../UTIcon';
|
|
9
|
+
import UTLabel from '../../../../../UTLabel';
|
|
10
|
+
import UTTooltip from '../../../../../UTTooltip';
|
|
11
|
+
|
|
12
|
+
import { ERROR_ICON, KEYBOARD_BY_TYPE, TYPES } from './constants';
|
|
13
|
+
import { iconRenderer } from './utils';
|
|
14
|
+
import { retrieveStyle } from './theme';
|
|
15
|
+
|
|
16
|
+
const TextInputField = ({
|
|
17
|
+
action,
|
|
18
|
+
alwaysShowPlaceholder,
|
|
19
|
+
disabled,
|
|
20
|
+
error,
|
|
21
|
+
inputSize,
|
|
22
|
+
LeftIcon,
|
|
23
|
+
maxCount,
|
|
24
|
+
maxRows,
|
|
25
|
+
onBlur,
|
|
26
|
+
onChange,
|
|
27
|
+
onFocus,
|
|
28
|
+
placeholder,
|
|
29
|
+
prefix,
|
|
30
|
+
readOnly,
|
|
31
|
+
RightIcon,
|
|
32
|
+
style,
|
|
33
|
+
suffix,
|
|
34
|
+
theme,
|
|
35
|
+
tooltip,
|
|
36
|
+
type,
|
|
37
|
+
value
|
|
38
|
+
}) => {
|
|
39
|
+
const [displayValue, setDisplayValue] = useState(value);
|
|
40
|
+
const [focused, setFocused] = useState(false);
|
|
41
|
+
const [inputWidth, setInputWidth] = useState(null);
|
|
42
|
+
const [internalValue, setInternalValue] = useState(value);
|
|
43
|
+
const textInputRef = useRef(null);
|
|
44
|
+
|
|
45
|
+
const multiline = maxRows > 1;
|
|
46
|
+
|
|
47
|
+
const handleTextChange = useCallback(
|
|
48
|
+
text => {
|
|
49
|
+
setInternalValue(text);
|
|
50
|
+
onChange?.(text);
|
|
51
|
+
},
|
|
52
|
+
[onChange]
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const handleFocus = useCallback(
|
|
56
|
+
event => {
|
|
57
|
+
setFocused(true);
|
|
58
|
+
if (!multiline) setDisplayValue(internalValue);
|
|
59
|
+
onFocus?.(event);
|
|
60
|
+
},
|
|
61
|
+
[onFocus, internalValue, multiline]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const handleBlur = useCallback(
|
|
65
|
+
event => {
|
|
66
|
+
const truncateText = text => {
|
|
67
|
+
if (!inputWidth || !text) return text;
|
|
68
|
+
|
|
69
|
+
const charWidth = 8;
|
|
70
|
+
const maxCharsPerLine = Math.floor(inputWidth / charWidth);
|
|
71
|
+
const maxLength = maxCharsPerLine;
|
|
72
|
+
|
|
73
|
+
return text.length > maxLength ? `${text.substring(0, maxLength - 3)}...` : text;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
setFocused(false);
|
|
77
|
+
if (!multiline) setDisplayValue(truncateText(internalValue));
|
|
78
|
+
onBlur?.(event);
|
|
79
|
+
},
|
|
80
|
+
[inputWidth, internalValue, multiline, onBlur]
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const { actionStyle, containerStyle, inputRowStyle, inputStyle, textLengthRowStyle, placeholderColor } =
|
|
84
|
+
retrieveStyle({
|
|
85
|
+
disabled,
|
|
86
|
+
error,
|
|
87
|
+
focused,
|
|
88
|
+
inputSize,
|
|
89
|
+
maxRows,
|
|
90
|
+
multiline,
|
|
91
|
+
readOnly,
|
|
92
|
+
style,
|
|
93
|
+
theme
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
setInternalValue(value);
|
|
98
|
+
setDisplayValue(value);
|
|
99
|
+
}, [value]);
|
|
100
|
+
|
|
101
|
+
useLayoutEffect(() => {
|
|
102
|
+
if (textInputRef.current) {
|
|
103
|
+
textInputRef.current.measure((_x, _y, width) => {
|
|
104
|
+
setInputWidth(width);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}, [focused]);
|
|
108
|
+
|
|
109
|
+
const secureTextEntry = type === TYPES.PASSWORD;
|
|
110
|
+
const autoCapitalize = type === TYPES.EMAIL ? 'none' : 'sentences';
|
|
111
|
+
const keyboardType = KEYBOARD_BY_TYPE[type] || 'default';
|
|
112
|
+
|
|
113
|
+
const renderIcon = iconRenderer(error, inputStyle.color);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<View style={containerStyle}>
|
|
117
|
+
<View style={inputRowStyle}>
|
|
118
|
+
{LeftIcon && renderIcon(LeftIcon)}
|
|
119
|
+
{prefix && <UTLabel colorTheme="gray">{prefix}</UTLabel>}
|
|
120
|
+
<TextInput
|
|
121
|
+
autoCapitalize={autoCapitalize}
|
|
122
|
+
autoCorrect={false}
|
|
123
|
+
editable={!disabled && !readOnly}
|
|
124
|
+
keyboardType={keyboardType}
|
|
125
|
+
maxLength={maxCount}
|
|
126
|
+
multiline={multiline}
|
|
127
|
+
numberOfLines={maxRows}
|
|
128
|
+
onChangeText={handleTextChange}
|
|
129
|
+
onEndEditing={handleBlur}
|
|
130
|
+
onFocus={handleFocus}
|
|
131
|
+
placeholder={!focused && !alwaysShowPlaceholder ? '' : placeholder}
|
|
132
|
+
placeholderTextColor={placeholderColor}
|
|
133
|
+
ref={textInputRef}
|
|
134
|
+
secureTextEntry={secureTextEntry}
|
|
135
|
+
style={inputStyle}
|
|
136
|
+
type={type}
|
|
137
|
+
value={focused || multiline ? internalValue : displayValue}
|
|
138
|
+
/>
|
|
139
|
+
{suffix && (
|
|
140
|
+
<UTLabel colorTheme="gray" variant="small">
|
|
141
|
+
{suffix}
|
|
142
|
+
</UTLabel>
|
|
143
|
+
)}
|
|
144
|
+
{action && (
|
|
145
|
+
<UTButton
|
|
146
|
+
disabled={disabled}
|
|
147
|
+
style={actionStyle}
|
|
148
|
+
variant="text"
|
|
149
|
+
colorTheme={action.colorTheme}
|
|
150
|
+
Icon={action.icon}
|
|
151
|
+
onPress={action.onPress}
|
|
152
|
+
>
|
|
153
|
+
{action.text}
|
|
154
|
+
</UTButton>
|
|
155
|
+
)}
|
|
156
|
+
{error ? renderIcon(ERROR_ICON) : RightIcon && renderIcon(RightIcon)}
|
|
157
|
+
{!!tooltip && (
|
|
158
|
+
<UTTooltip content={<UTLabel>{tooltip}</UTLabel>}>
|
|
159
|
+
<UTIcon name="IconInfoCircle" color="gray" />
|
|
160
|
+
</UTTooltip>
|
|
161
|
+
)}
|
|
162
|
+
</View>
|
|
163
|
+
{maxCount && (
|
|
164
|
+
<View style={textLengthRowStyle}>
|
|
165
|
+
<UTLabel colorTheme="gray" variant="small">
|
|
166
|
+
{internalValue?.length || 0}/{maxCount}
|
|
167
|
+
</UTLabel>
|
|
168
|
+
</View>
|
|
169
|
+
)}
|
|
170
|
+
</View>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
TextInputField.propTypes = {
|
|
175
|
+
action: shape({ icon: elementType, action: func, colorTheme: string, text: string }),
|
|
176
|
+
alwaysShowPlaceholder: bool,
|
|
177
|
+
disabled: bool,
|
|
178
|
+
error: string,
|
|
179
|
+
inputSize: string,
|
|
180
|
+
LeftIcon: elementType,
|
|
181
|
+
maxCount: number,
|
|
182
|
+
maxRows: number,
|
|
183
|
+
onBlur: func,
|
|
184
|
+
onChange: func,
|
|
185
|
+
onFocus: func,
|
|
186
|
+
placeholder: string,
|
|
187
|
+
prefix: string,
|
|
188
|
+
readOnly: bool,
|
|
189
|
+
RightIcon: elementType,
|
|
190
|
+
style: shape({
|
|
191
|
+
container: ViewPropTypes.style,
|
|
192
|
+
input: ViewPropTypes.style,
|
|
193
|
+
root: ViewPropTypes.style
|
|
194
|
+
}),
|
|
195
|
+
suffix: string,
|
|
196
|
+
theme: themeType,
|
|
197
|
+
tooltip: string,
|
|
198
|
+
type: string,
|
|
199
|
+
value: string
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export default withTheme(TextInputField);
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
import { SMALL } from '../../constants';
|
|
4
|
+
|
|
5
|
+
const LINE_HEIGHT = 22;
|
|
6
|
+
|
|
7
|
+
const baseTextInputTheme = theme => ({
|
|
8
|
+
action: {
|
|
9
|
+
root: {
|
|
10
|
+
paddingHorizontal: 0,
|
|
11
|
+
paddingVertical: 0
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
container: {
|
|
15
|
+
alignItems: 'center',
|
|
16
|
+
backgroundColor: 'transparent',
|
|
17
|
+
borderColor: theme.Palette.light['05'],
|
|
18
|
+
borderRadius: 4,
|
|
19
|
+
borderWidth: 1,
|
|
20
|
+
flex: 1,
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
paddingHorizontal: 16,
|
|
23
|
+
paddingVertical: 12,
|
|
24
|
+
rowGap: 16,
|
|
25
|
+
width: '100%'
|
|
26
|
+
},
|
|
27
|
+
input: {
|
|
28
|
+
color: theme.Palette.dark['05'],
|
|
29
|
+
flex: 1,
|
|
30
|
+
fontFamily: theme.fonts.fontFamily, // TODO! Change when integration of font sizes
|
|
31
|
+
fontSize: 16,
|
|
32
|
+
lineHeight: LINE_HEIGHT,
|
|
33
|
+
padding: 0,
|
|
34
|
+
zIndex: 4,
|
|
35
|
+
...Platform.select({
|
|
36
|
+
ios: {
|
|
37
|
+
height: '100%',
|
|
38
|
+
lineHeight: LINE_HEIGHT + 4
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
},
|
|
42
|
+
inputRow: {
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
columnGap: 16,
|
|
45
|
+
flex: 1,
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
justifyContent: 'space-between',
|
|
48
|
+
width: '100%'
|
|
49
|
+
},
|
|
50
|
+
textLengthRow: {
|
|
51
|
+
flexDirection: 'row',
|
|
52
|
+
justifyContent: 'flex-start',
|
|
53
|
+
width: '100%'
|
|
54
|
+
},
|
|
55
|
+
placeholder: {
|
|
56
|
+
color: theme.Palette.gray['02']
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const conditionalContainerStyle = (focused, error, theme, readOnly, disabled, inputSize) => ({
|
|
61
|
+
...(focused && {
|
|
62
|
+
borderColor: theme.Palette.accent['04'],
|
|
63
|
+
borderWidth: 2,
|
|
64
|
+
padding: -1
|
|
65
|
+
}),
|
|
66
|
+
...(error && {
|
|
67
|
+
borderColor: theme.Palette.error['04']
|
|
68
|
+
}),
|
|
69
|
+
...(readOnly && {
|
|
70
|
+
backgroundColor: 'transparent',
|
|
71
|
+
borderColor: 'transparent',
|
|
72
|
+
paddingHorizontal: 0
|
|
73
|
+
}),
|
|
74
|
+
...(disabled && {
|
|
75
|
+
backgroundColor: theme.Palette.light['03'],
|
|
76
|
+
borderColor: 'transparent'
|
|
77
|
+
}),
|
|
78
|
+
...(inputSize === SMALL && {
|
|
79
|
+
paddingHorizontal: 12,
|
|
80
|
+
paddingVertical: 8
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const conditionalInputStyle = (maxRows, multiline) => ({
|
|
85
|
+
...(multiline && {
|
|
86
|
+
height: maxRows * LINE_HEIGHT,
|
|
87
|
+
textAlignVertical: 'top'
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export const retrieveStyle = ({
|
|
92
|
+
disabled,
|
|
93
|
+
error,
|
|
94
|
+
focused,
|
|
95
|
+
inputSize,
|
|
96
|
+
maxRows,
|
|
97
|
+
multiline,
|
|
98
|
+
readOnly,
|
|
99
|
+
style = {},
|
|
100
|
+
theme
|
|
101
|
+
}) => {
|
|
102
|
+
const baseTheme = baseTextInputTheme(theme);
|
|
103
|
+
|
|
104
|
+
const containerStyle = {
|
|
105
|
+
...baseTheme.container,
|
|
106
|
+
...conditionalContainerStyle(focused, error, theme, readOnly, disabled, inputSize),
|
|
107
|
+
...style.container
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const inputStyle = {
|
|
111
|
+
...baseTheme.input,
|
|
112
|
+
...conditionalInputStyle(maxRows, multiline),
|
|
113
|
+
...style.input
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
actionStyle: { ...baseTheme.action, ...style.action },
|
|
118
|
+
inputStyle,
|
|
119
|
+
containerStyle,
|
|
120
|
+
inputRowStyle: baseTheme.inputRow,
|
|
121
|
+
textLengthRowStyle: baseTheme.textLengthRow,
|
|
122
|
+
placeholderColor: baseTheme.placeholder.color
|
|
123
|
+
};
|
|
124
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import UTIcon from '../../../../../UTIcon';
|
|
4
|
+
|
|
5
|
+
const isUTIcon = icon => typeof icon === 'string';
|
|
6
|
+
|
|
7
|
+
export const iconRenderer = (error, themeColor) => Icon => {
|
|
8
|
+
const color = error ? 'error' : 'gray';
|
|
9
|
+
const shade = error && '04';
|
|
10
|
+
return isUTIcon(Icon) ? <UTIcon name={Icon} color={color} shade={shade} /> : <Icon fill={themeColor} />;
|
|
11
|
+
};
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import UTLabel from '../../../UTLabel';
|
|
5
|
+
import UTValidation from '../../../UTValidation';
|
|
6
|
+
|
|
7
|
+
import { DEFAULT_PROPS, REQUIRED_LABEL, SMALL } from './constants';
|
|
8
|
+
import { formatErrorToValidation } from './utils';
|
|
9
|
+
import propTypes from './proptypes';
|
|
10
|
+
import styles from './styles';
|
|
11
|
+
import TextInputField from './components/TextInputField';
|
|
12
|
+
|
|
13
|
+
const UTTextInput = ({
|
|
14
|
+
action,
|
|
15
|
+
alwaysShowPlaceholder,
|
|
16
|
+
disabled,
|
|
17
|
+
error,
|
|
18
|
+
helpText,
|
|
19
|
+
inputSize,
|
|
20
|
+
label,
|
|
21
|
+
labelVariant,
|
|
22
|
+
LeftIcon,
|
|
23
|
+
maxCount,
|
|
24
|
+
maxRows,
|
|
25
|
+
onBlur,
|
|
26
|
+
onChange,
|
|
27
|
+
onFocus,
|
|
28
|
+
placeholder,
|
|
29
|
+
prefix,
|
|
30
|
+
readOnly,
|
|
31
|
+
required,
|
|
32
|
+
RightIcon,
|
|
33
|
+
style,
|
|
34
|
+
suffix,
|
|
35
|
+
tooltip,
|
|
36
|
+
type,
|
|
37
|
+
validations,
|
|
38
|
+
value
|
|
39
|
+
}) => {
|
|
40
|
+
const labelColorTheme = error ? 'error' : readOnly ? 'gray' : 'dark';
|
|
41
|
+
const labelComponentVariant = labelVariant === SMALL ? 'small' : 'body';
|
|
42
|
+
const validationData = validations || formatErrorToValidation(error);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<View style={[styles.container, style.root]}>
|
|
46
|
+
{label && (
|
|
47
|
+
<View style={styles.label}>
|
|
48
|
+
<UTLabel colorTheme={labelColorTheme} shade={error && '04'} variant={labelComponentVariant}>
|
|
49
|
+
{label}
|
|
50
|
+
</UTLabel>
|
|
51
|
+
{required && (
|
|
52
|
+
<UTLabel colorTheme="error" shade="04" variant={labelComponentVariant}>
|
|
53
|
+
{REQUIRED_LABEL}
|
|
54
|
+
</UTLabel>
|
|
55
|
+
)}
|
|
56
|
+
</View>
|
|
57
|
+
)}
|
|
58
|
+
<TextInputField
|
|
59
|
+
action={action}
|
|
60
|
+
alwaysShowPlaceholder={alwaysShowPlaceholder}
|
|
61
|
+
disabled={disabled}
|
|
62
|
+
error={error}
|
|
63
|
+
inputSize={inputSize}
|
|
64
|
+
LeftIcon={LeftIcon}
|
|
65
|
+
maxCount={maxCount}
|
|
66
|
+
maxRows={maxRows}
|
|
67
|
+
onBlur={onBlur}
|
|
68
|
+
onChange={onChange}
|
|
69
|
+
onFocus={onFocus}
|
|
70
|
+
placeholder={placeholder}
|
|
71
|
+
prefix={prefix}
|
|
72
|
+
readOnly={readOnly}
|
|
73
|
+
RightIcon={RightIcon}
|
|
74
|
+
style={style}
|
|
75
|
+
suffix={suffix}
|
|
76
|
+
tooltip={tooltip}
|
|
77
|
+
type={type}
|
|
78
|
+
value={value}
|
|
79
|
+
/>
|
|
80
|
+
{helpText && (
|
|
81
|
+
<UTLabel colorTheme="gray" variant="small">
|
|
82
|
+
{helpText}
|
|
83
|
+
</UTLabel>
|
|
84
|
+
)}
|
|
85
|
+
{error && <UTValidation validationData={validationData} />}
|
|
86
|
+
</View>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
UTTextInput.defaultProps = DEFAULT_PROPS;
|
|
91
|
+
|
|
92
|
+
UTTextInput.propTypes = propTypes;
|
|
93
|
+
|
|
94
|
+
export default UTTextInput;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { bool, elementType, func, number, shape, string } from 'prop-types';
|
|
2
|
+
import { ViewPropTypes } from 'deprecated-react-native-prop-types';
|
|
3
|
+
|
|
4
|
+
import { validationDataProptypes } from '../../../UTValidation/constants';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
action: shape({ icon: elementType, action: func, colorTheme: string, text: string }),
|
|
8
|
+
alwaysShowPlaceholder: bool,
|
|
9
|
+
disabled: bool,
|
|
10
|
+
error: string,
|
|
11
|
+
helpText: string,
|
|
12
|
+
inputSize: string,
|
|
13
|
+
label: string,
|
|
14
|
+
labelVariant: string,
|
|
15
|
+
LeftIcon: elementType,
|
|
16
|
+
maxCount: number,
|
|
17
|
+
maxRows: number,
|
|
18
|
+
onBlur: func,
|
|
19
|
+
onChange: func,
|
|
20
|
+
onFocus: func,
|
|
21
|
+
placeholder: string,
|
|
22
|
+
prefix: string,
|
|
23
|
+
readOnly: bool,
|
|
24
|
+
required: bool,
|
|
25
|
+
RightIcon: elementType,
|
|
26
|
+
style: shape({
|
|
27
|
+
container: ViewPropTypes.style,
|
|
28
|
+
input: ViewPropTypes.style,
|
|
29
|
+
root: ViewPropTypes.style
|
|
30
|
+
}),
|
|
31
|
+
suffix: string,
|
|
32
|
+
tooltip: string,
|
|
33
|
+
type: string,
|
|
34
|
+
validations: validationDataProptypes,
|
|
35
|
+
value: string
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const formatErrorToValidation = error => [{ items: [{ text: error, status: 'error' }] }];
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# UTValidation
|
|
2
|
+
|
|
3
|
+
### Description
|
|
4
|
+
|
|
5
|
+
`UTValidation` is a component designed to display input validation messages. The statuses of the messages can be information, error, or success, helping users understand the current state of their input.
|
|
6
|
+
|
|
7
|
+
### Validation Data
|
|
8
|
+
|
|
9
|
+
The `validationData` prop is an array of objects, each representing a validation message or group of messages. Each object can optionally have a `title` and must have a `status` and an `items` array. The `items` array contains objects, each with `text` and `status` properties.
|
|
10
|
+
|
|
11
|
+
#### Structure of validationData
|
|
12
|
+
|
|
13
|
+
- title (optional): A string that represents the title of the validation group.
|
|
14
|
+
- status (required): A string indicating the status of the validation group. Possible values are success, error, and default.
|
|
15
|
+
- items (required): An array of objects, each representing an individual validation item.
|
|
16
|
+
- text (required): A string containing the validation message.
|
|
17
|
+
- status (required): A string indicating the status of the validation item. Possible values are success, error, and default.
|
|
18
|
+
|
|
19
|
+
Here's an example of `validationData`:
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
const validationData = [
|
|
23
|
+
{
|
|
24
|
+
items: [
|
|
25
|
+
{
|
|
26
|
+
status: 'success',
|
|
27
|
+
text: 'Debe tener como mínimo 8 caracteres.'
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
title: 'Debe cumplir con al menos 1 de las siguientes condiciones:',
|
|
33
|
+
status: 'error',
|
|
34
|
+
items: [
|
|
35
|
+
{
|
|
36
|
+
status: 'default',
|
|
37
|
+
text: 'Al menos 1 letra minúscula.'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
status: 'default',
|
|
41
|
+
text: 'Al menos 1 letra mayúscula.'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
status: 'success',
|
|
45
|
+
text: 'Al menos 1 número.'
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Props
|
|
53
|
+
|
|
54
|
+
| Name | Description | Default |
|
|
55
|
+
| -------------- | ------------------- | ------- |
|
|
56
|
+
| validationData | The data to display | {} |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { arrayOf, shape, string } from 'prop-types';
|
|
2
|
+
|
|
3
|
+
export const STATUSES = {
|
|
4
|
+
success: 'success',
|
|
5
|
+
error: 'error',
|
|
6
|
+
default: 'default'
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const STATUS_COLOR_MAPPER = {
|
|
10
|
+
[STATUSES.success]: 'success',
|
|
11
|
+
[STATUSES.error]: 'error',
|
|
12
|
+
[STATUSES.default]: 'gray'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const ICON_MAPPER = {
|
|
16
|
+
[STATUSES.success]: 'IconCheck',
|
|
17
|
+
[STATUSES.error]: 'IconX'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const STATUSES_WITH_ICON = [STATUSES.success, STATUSES.error];
|
|
21
|
+
|
|
22
|
+
export const ICON_SIZE = 15;
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_PROPS = {
|
|
25
|
+
validationData: {}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const validationDataProptypes = arrayOf(
|
|
29
|
+
shape({
|
|
30
|
+
items: arrayOf(
|
|
31
|
+
shape({
|
|
32
|
+
text: string,
|
|
33
|
+
status: string
|
|
34
|
+
})
|
|
35
|
+
),
|
|
36
|
+
status: string,
|
|
37
|
+
title: string
|
|
38
|
+
})
|
|
39
|
+
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { isEmpty } from 'lodash';
|
|
3
|
+
import { View } from 'react-native';
|
|
4
|
+
import { ViewPropTypes } from 'deprecated-react-native-prop-types';
|
|
5
|
+
|
|
6
|
+
import { themeType, withTheme } from '../../theming';
|
|
7
|
+
import UTIcon from '../UTIcon';
|
|
8
|
+
import UTLabel from '../UTLabel';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_PROPS,
|
|
12
|
+
ICON_MAPPER,
|
|
13
|
+
ICON_SIZE,
|
|
14
|
+
STATUS_COLOR_MAPPER,
|
|
15
|
+
STATUSES_WITH_ICON,
|
|
16
|
+
validationDataProptypes
|
|
17
|
+
} from './constants';
|
|
18
|
+
import { retrieveStyle } from './theme';
|
|
19
|
+
import styles from './styles';
|
|
20
|
+
import { generateItemKey, generateKey } from './utils';
|
|
21
|
+
|
|
22
|
+
const UTValidation = ({ theme, validationData, style }) => {
|
|
23
|
+
if (isEmpty(validationData)) return null;
|
|
24
|
+
|
|
25
|
+
const themeStyles = retrieveStyle({ theme });
|
|
26
|
+
|
|
27
|
+
const showIcon = status => STATUSES_WITH_ICON.includes(status);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<View style={[style, styles.container]}>
|
|
31
|
+
{validationData.map(({ items, status, title }, index) => (
|
|
32
|
+
<View key={generateKey({ items, status, title }, index)} style={styles.validationContainer}>
|
|
33
|
+
{title && (
|
|
34
|
+
<UTLabel colorTheme={STATUS_COLOR_MAPPER[status]} variant="small">
|
|
35
|
+
{title}
|
|
36
|
+
</UTLabel>
|
|
37
|
+
)}
|
|
38
|
+
<View style={styles.itemsContainer}>
|
|
39
|
+
{items.map(({ text, status: itemStatus }, itemIndex) => (
|
|
40
|
+
<View
|
|
41
|
+
key={generateItemKey({ text, status: itemStatus }, itemIndex)}
|
|
42
|
+
style={styles.itemContainer}
|
|
43
|
+
>
|
|
44
|
+
<View style={[styles.iconContainer, themeStyles.icon[itemStatus]]}>
|
|
45
|
+
{showIcon(itemStatus) && (
|
|
46
|
+
<UTIcon color={itemStatus} name={ICON_MAPPER[itemStatus]} size={ICON_SIZE} />
|
|
47
|
+
)}
|
|
48
|
+
</View>
|
|
49
|
+
<UTLabel colorTheme={STATUS_COLOR_MAPPER[itemStatus]} variant="small">
|
|
50
|
+
{text}
|
|
51
|
+
</UTLabel>
|
|
52
|
+
</View>
|
|
53
|
+
))}
|
|
54
|
+
</View>
|
|
55
|
+
</View>
|
|
56
|
+
))}
|
|
57
|
+
</View>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
UTValidation.defaultProps = DEFAULT_PROPS;
|
|
62
|
+
|
|
63
|
+
UTValidation.propTypes = {
|
|
64
|
+
style: ViewPropTypes.style,
|
|
65
|
+
theme: themeType,
|
|
66
|
+
validationData: validationDataProptypes
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default withTheme(UTValidation);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export default StyleSheet.create({
|
|
4
|
+
container: {
|
|
5
|
+
rowGap: 16
|
|
6
|
+
},
|
|
7
|
+
iconContainer: {
|
|
8
|
+
alignItems: 'center',
|
|
9
|
+
borderRadius: 100,
|
|
10
|
+
height: 20,
|
|
11
|
+
justifyContent: 'center',
|
|
12
|
+
marginRight: 8,
|
|
13
|
+
width: 20
|
|
14
|
+
},
|
|
15
|
+
itemContainer: {
|
|
16
|
+
alignItems: 'center',
|
|
17
|
+
flexDirection: 'row'
|
|
18
|
+
},
|
|
19
|
+
itemsContainer: {
|
|
20
|
+
rowGap: 8
|
|
21
|
+
},
|
|
22
|
+
validationContainer: {
|
|
23
|
+
rowGap: 12
|
|
24
|
+
}
|
|
25
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { STATUSES, STATUS_COLOR_MAPPER } from './constants';
|
|
2
|
+
|
|
3
|
+
const variantsColorTheme = (colorTheme, theme) => theme.Palette[colorTheme];
|
|
4
|
+
|
|
5
|
+
export const retrieveStyle = ({ theme }) => ({
|
|
6
|
+
icon: {
|
|
7
|
+
[STATUSES.success]: {
|
|
8
|
+
backgroundColor: variantsColorTheme(STATUS_COLOR_MAPPER[STATUSES.success], theme)['01']
|
|
9
|
+
},
|
|
10
|
+
[STATUSES.error]: {
|
|
11
|
+
backgroundColor: variantsColorTheme(STATUS_COLOR_MAPPER[STATUSES.error], theme)['01']
|
|
12
|
+
},
|
|
13
|
+
[STATUSES.default]: {
|
|
14
|
+
backgroundColor: variantsColorTheme(STATUS_COLOR_MAPPER[STATUSES.default], theme)['01']
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
});
|