ikualo-ui-kit-mobile 0.0.3 → 0.0.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/.expo/README.md +8 -0
- package/assets/images/_images.ts +5 -0
- package/assets/images/headerBack.png +0 -0
- package/assets/images/headerClose.png +0 -0
- package/assets/images/headerInfo.png +0 -0
- package/assets/styles/elements/buttons.ts +16 -1
- package/package.json +2 -5
- package/src/config/paper.config.ts +2 -1
- package/src/elements/buttons/BtnLightSmall.tsx +12 -5
- package/src/elements/cards/CardList.tsx +5 -4
- package/src/elements/headers/HeaderPage.tsx +35 -15
- package/src/elements/inputs/Input.tsx +37 -13
- package/src/elements/inputs/InputPassword.tsx +3 -0
package/.expo/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
> Why do I have a folder named ".expo" in my project?
|
|
2
|
+
The ".expo" folder is created when an Expo project is started using "expo start" command.
|
|
3
|
+
> What do the files contain?
|
|
4
|
+
- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
|
|
5
|
+
- "settings.json": contains the server configuration that is used to serve the application manifest.
|
|
6
|
+
> Should I commit the ".expo" folder?
|
|
7
|
+
No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
|
|
8
|
+
Upon project creation, the ".expo" folder is already added to your ".gitignore" file.
|
package/assets/images/_images.ts
CHANGED
|
@@ -45,4 +45,9 @@ export const images = {
|
|
|
45
45
|
},
|
|
46
46
|
validateIdentity: require('./validateIdentity.png'),
|
|
47
47
|
validationFailed: require('./validationFailed.png'),
|
|
48
|
+
header:{
|
|
49
|
+
back: require('./headerBack.png'),
|
|
50
|
+
close: require('./headerClose.png'),
|
|
51
|
+
help: require('./headerInfo.png'),
|
|
52
|
+
}
|
|
48
53
|
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -156,6 +156,7 @@ const stylesBtnLightSmall = StyleSheet.create({
|
|
|
156
156
|
minHeight: 48,
|
|
157
157
|
width: 48,
|
|
158
158
|
height: 48,
|
|
159
|
+
position: 'relative',
|
|
159
160
|
},
|
|
160
161
|
'btn-light-small-content': {
|
|
161
162
|
alignSelf: 'center',
|
|
@@ -166,7 +167,6 @@ const stylesBtnLightSmall = StyleSheet.create({
|
|
|
166
167
|
minHeight: 48,
|
|
167
168
|
width: 48,
|
|
168
169
|
height: 48,
|
|
169
|
-
marginLeft: 12,
|
|
170
170
|
},
|
|
171
171
|
'btn-light-small-label': {
|
|
172
172
|
fontSize: 24,
|
|
@@ -183,8 +183,23 @@ const stylesBtnLightSmall = StyleSheet.create({
|
|
|
183
183
|
width: 48,
|
|
184
184
|
height: 48,
|
|
185
185
|
},
|
|
186
|
+
'btn-light-small--active': {
|
|
187
|
+
backgroundColor: theme.colors.secondaryLight,
|
|
188
|
+
borderColor: theme.colors.secondary,
|
|
189
|
+
borderRadius: 8,
|
|
190
|
+
borderWidth: 1,
|
|
191
|
+
maxWidth: 48,
|
|
192
|
+
minWidth: 48,
|
|
193
|
+
maxHeight: 48,
|
|
194
|
+
minHeight: 48,
|
|
195
|
+
width: 48,
|
|
196
|
+
height: 48,
|
|
197
|
+
},
|
|
186
198
|
'btn-light-small-label--disabled': {
|
|
187
199
|
color: theme.colors.disabledButtonBorder,
|
|
188
200
|
},
|
|
201
|
+
'btn-light-small-label--active': {
|
|
202
|
+
color: theme.colors.secondary,
|
|
203
|
+
},
|
|
189
204
|
});
|
|
190
205
|
export { stylesButtonContained, stylesButtonOutlined, stylesButtonText, stylesBtnLightSmall };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ikualo-ui-kit-mobile",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "expo start",
|
|
@@ -27,11 +27,9 @@
|
|
|
27
27
|
"expo-secure-store": "~13.0.2",
|
|
28
28
|
"expo-status-bar": "~1.12.1",
|
|
29
29
|
"expo-updates": "^0.25.14",
|
|
30
|
-
"i18next": "^23.11.5",
|
|
31
30
|
"p-retry": "^6.2.0",
|
|
32
31
|
"react": "18.2.0",
|
|
33
32
|
"react-dom": "18.2.0",
|
|
34
|
-
"react-i18next": "^14.1.2",
|
|
35
33
|
"react-native": "0.74.1",
|
|
36
34
|
"react-native-asset": "^2.1.1",
|
|
37
35
|
"react-native-country-flag": "^2.0.2",
|
|
@@ -40,8 +38,7 @@
|
|
|
40
38
|
"react-native-paper": "^5.12.3",
|
|
41
39
|
"react-native-safe-area-context": "^4.10.1",
|
|
42
40
|
"react-native-screens": "^3.31.1",
|
|
43
|
-
"react-native-web": "~0.19.10"
|
|
44
|
-
"zustand": "^4.5.2"
|
|
41
|
+
"react-native-web": "~0.19.10"
|
|
45
42
|
},
|
|
46
43
|
"devDependencies": {
|
|
47
44
|
"@babel/cli": "^7.24.8",
|
|
@@ -1,31 +1,38 @@
|
|
|
1
|
+
|
|
1
2
|
import { Button, Text } from 'react-native-paper';
|
|
2
3
|
import { stylesBtnLightSmall } from '../../../assets/styles/elements/buttons';
|
|
4
|
+
import { View } from 'react-native';
|
|
5
|
+
import { cloneElement } from 'react';
|
|
3
6
|
|
|
4
7
|
interface IfBtnLightSmall {
|
|
5
8
|
onPress: () => void;
|
|
6
|
-
icon:
|
|
9
|
+
icon: JSX.Element;
|
|
7
10
|
disabled?: boolean;
|
|
11
|
+
active?: boolean;
|
|
8
12
|
}
|
|
9
13
|
export const BtnLightSmall = (props: IfBtnLightSmall) => {
|
|
10
|
-
const { onPress, icon, disabled = false } = props;
|
|
14
|
+
const { onPress, icon, disabled = false,active } = props;
|
|
11
15
|
const anything = '';
|
|
12
|
-
|
|
13
16
|
return (
|
|
14
17
|
<Button
|
|
15
18
|
style={[
|
|
16
19
|
stylesBtnLightSmall['btn-light-small'],
|
|
20
|
+
active && stylesBtnLightSmall['btn-light-small--active'],
|
|
17
21
|
disabled && stylesBtnLightSmall['btn-light-small--disabled'],
|
|
18
22
|
]}
|
|
19
23
|
contentStyle={stylesBtnLightSmall['btn-light-small-content']}
|
|
20
24
|
labelStyle={[
|
|
21
25
|
(stylesBtnLightSmall['btn-light-small-label'],
|
|
26
|
+
active && stylesBtnLightSmall['btn-light-small-label--active'],
|
|
22
27
|
disabled && stylesBtnLightSmall['btn-light-small-label--disabled']),
|
|
23
28
|
]}
|
|
24
29
|
onPress={onPress}
|
|
25
|
-
icon={icon}
|
|
26
30
|
disabled={disabled}
|
|
27
31
|
>
|
|
28
|
-
<
|
|
32
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
|
|
33
|
+
{cloneElement(icon, { size:24,color: active?stylesBtnLightSmall['btn-light-small--active'].borderColor: stylesBtnLightSmall['btn-light-small'].borderColor })}
|
|
34
|
+
<Text>{anything}</Text>
|
|
35
|
+
</View>
|
|
29
36
|
</Button>
|
|
30
37
|
);
|
|
31
38
|
};
|
|
@@ -2,9 +2,10 @@ import { Card } from 'react-native-paper';
|
|
|
2
2
|
import { stylesCards } from '../../../assets/styles/elements/cards';
|
|
3
3
|
import { View, Text, TouchableHighlight } from 'react-native';
|
|
4
4
|
import { useState } from 'react';
|
|
5
|
+
import { ScrollView } from 'react-native-gesture-handler';
|
|
5
6
|
|
|
6
7
|
interface IfCardList {
|
|
7
|
-
options: { value: string }[];
|
|
8
|
+
options: { value: string | JSX.Element, id: string }[];
|
|
8
9
|
children?: React.ReactNode;
|
|
9
10
|
onSelectOption: (value: string) => void;
|
|
10
11
|
}
|
|
@@ -14,13 +15,13 @@ export const CardList = (props: IfCardList) => {
|
|
|
14
15
|
const [selectedOption, setSelectOption] = useState(0);
|
|
15
16
|
|
|
16
17
|
return (
|
|
17
|
-
<
|
|
18
|
+
<ScrollView>
|
|
18
19
|
{options.map((option, index) => (
|
|
19
20
|
<TouchableHighlight
|
|
20
21
|
key={index}
|
|
21
22
|
onPress={() => {
|
|
22
23
|
setSelectOption(index);
|
|
23
|
-
onSelectOption(option.
|
|
24
|
+
onSelectOption(option.id);
|
|
24
25
|
}}
|
|
25
26
|
style={stylesCards['card-list-item']}
|
|
26
27
|
>
|
|
@@ -34,6 +35,6 @@ export const CardList = (props: IfCardList) => {
|
|
|
34
35
|
</Card>
|
|
35
36
|
</TouchableHighlight>
|
|
36
37
|
))}
|
|
37
|
-
</
|
|
38
|
+
</ScrollView>
|
|
38
39
|
);
|
|
39
40
|
};
|
|
@@ -2,32 +2,52 @@ import { Text } from 'react-native-paper';
|
|
|
2
2
|
import { Image, TouchableOpacity, View } from 'react-native';
|
|
3
3
|
import { useNavigation } from '@react-navigation/native';
|
|
4
4
|
import { stylesHeader } from '../../../assets/styles/elements/header';
|
|
5
|
-
import { icons } from '../../../assets/icons/_icons';
|
|
6
5
|
import { images } from '../../../assets/images/_images';
|
|
7
6
|
|
|
8
7
|
interface IFHeaderPage {
|
|
9
8
|
title: string;
|
|
10
|
-
navigateTo: string;
|
|
11
|
-
|
|
9
|
+
navigateTo: 'Back' | string;
|
|
10
|
+
onPressBack?: () => void;
|
|
11
|
+
onPressRight?: () => void;
|
|
12
|
+
right: 'icon' | 'close' | 'help' | ''
|
|
12
13
|
}
|
|
13
14
|
export const HeaderPage = (props: IFHeaderPage) => {
|
|
14
|
-
const { navigateTo, title,
|
|
15
|
-
const
|
|
15
|
+
const { navigateTo, title, onPressBack, onPressRight, right } = props;
|
|
16
|
+
const { navigate, goBack } = useNavigation();
|
|
16
17
|
const navigateToScreen = () => {
|
|
17
|
-
|
|
18
|
-
navigateTo === 'Back' ?
|
|
18
|
+
onPressBack && onPressBack();
|
|
19
|
+
navigateTo === 'Back' ? goBack() : navigate(navigateTo as never);
|
|
19
20
|
};
|
|
21
|
+
const actionsRight = [
|
|
22
|
+
{
|
|
23
|
+
name: 'icon',
|
|
24
|
+
component: <Image source={images.logoSmallBlack} style={stylesHeader['header-logo']} />
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'close',
|
|
28
|
+
component: <TouchableOpacity onPress={()=>onPressRight && onPressRight()}>
|
|
29
|
+
<Image source={images.header.close} style={stylesHeader['header-logo']} />
|
|
30
|
+
</TouchableOpacity>
|
|
31
|
+
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'help',
|
|
35
|
+
component: <TouchableOpacity onPress={()=>onPressRight && onPressRight()}>
|
|
36
|
+
<Image source={images.header.help} style={stylesHeader['header-logo']} />
|
|
37
|
+
</TouchableOpacity>
|
|
38
|
+
},
|
|
39
|
+
]
|
|
20
40
|
return (
|
|
21
41
|
<View style={stylesHeader['header']}>
|
|
22
|
-
<TouchableOpacity onPress={navigateToScreen}
|
|
23
|
-
<Image source={
|
|
42
|
+
<TouchableOpacity onPress={navigateToScreen}>
|
|
43
|
+
<Image source={images.header.back} style={stylesHeader['header-logo']} />
|
|
24
44
|
</TouchableOpacity>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
<View style={{ flex: 1, alignItems: 'center' }}>
|
|
46
|
+
<Text variant="bodyMedium" style={stylesHeader['header-title']}>
|
|
47
|
+
{title}
|
|
48
|
+
</Text>
|
|
49
|
+
</View>
|
|
50
|
+
{actionsRight.find(x => x.name === right)?.component ?? ""}
|
|
31
51
|
</View>
|
|
32
52
|
);
|
|
33
53
|
};
|
|
@@ -28,10 +28,13 @@ interface IfInputText {
|
|
|
28
28
|
isValid?: boolean;
|
|
29
29
|
msgHelper?: string;
|
|
30
30
|
msgError?: string;
|
|
31
|
+
required?: boolean;
|
|
32
|
+
msgErrorRequired?: string;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export const Input = (props: IfInputText) => {
|
|
34
36
|
const [isFocus, setIsFocus] = useState<boolean>(false);
|
|
37
|
+
const [errorRequired, setErrorRequired] = useState<boolean>(false);
|
|
35
38
|
const {
|
|
36
39
|
label,
|
|
37
40
|
isDisabled,
|
|
@@ -53,15 +56,22 @@ export const Input = (props: IfInputText) => {
|
|
|
53
56
|
type,
|
|
54
57
|
placeholder,
|
|
55
58
|
pattern,
|
|
59
|
+
required,
|
|
60
|
+
msgErrorRequired
|
|
56
61
|
} = props;
|
|
57
62
|
const patternNumber = /^[0-9]*$/;
|
|
58
63
|
const patternAlphanumeric = /^[a-zA-Z0-9]*$/;
|
|
64
|
+
const patternLettersWithAccentsAndSpaces = /^[a-zA-ZáéíóúÁÉÍÓÚñÑ\s]*$/;
|
|
59
65
|
const onChangeText = (text: string) => {
|
|
60
66
|
onChange(text);
|
|
67
|
+
setErrorRequired(false);
|
|
61
68
|
if (type === 'number' && !patternNumber.test(text)) {
|
|
62
69
|
onInvalid && onInvalid();
|
|
63
70
|
} else if (type === 'alphanumeric' && !patternAlphanumeric.test(text)) {
|
|
64
71
|
onInvalid && onInvalid();
|
|
72
|
+
}
|
|
73
|
+
else if (type === 'text' && !patternLettersWithAccentsAndSpaces.test(text)) {
|
|
74
|
+
onInvalid && onInvalid();
|
|
65
75
|
} else if (pattern && !pattern.test(text)) {
|
|
66
76
|
onInvalid && onInvalid();
|
|
67
77
|
}
|
|
@@ -69,15 +79,17 @@ export const Input = (props: IfInputText) => {
|
|
|
69
79
|
return (
|
|
70
80
|
<View>
|
|
71
81
|
<TextInput
|
|
72
|
-
label={!isFocus && placeholder && value
|
|
82
|
+
label={!isFocus && placeholder && value?.trim() !== '' ? placeholder : label}
|
|
73
83
|
mode={mode ?? 'flat'}
|
|
74
84
|
disabled={isDisabled}
|
|
75
85
|
value={value}
|
|
76
86
|
onChangeText={onChangeText}
|
|
77
87
|
maxLength={maxLength}
|
|
88
|
+
selectionColor={!errorRequired ?styleInput['input'].borderBottomColor:styleInput['input-txt-error'].borderBottomColor}
|
|
78
89
|
theme={{
|
|
79
90
|
colors: {
|
|
80
|
-
onSurfaceVariant: styleInput['input-txt'].
|
|
91
|
+
onSurfaceVariant: !errorRequired ?styleInput['input'].borderBottomColor:styleInput['input-txt-error'].borderBottomColor,
|
|
92
|
+
error: styleInput['input-txt-error'].borderBottomColor,
|
|
81
93
|
},
|
|
82
94
|
}}
|
|
83
95
|
onFocus={() => {
|
|
@@ -87,44 +99,56 @@ export const Input = (props: IfInputText) => {
|
|
|
87
99
|
onBlur={() => {
|
|
88
100
|
setIsFocus(false);
|
|
89
101
|
onBlur && onBlur();
|
|
102
|
+
if (value?.trim() === '' && required) {
|
|
103
|
+
setErrorRequired(true);
|
|
104
|
+
onInvalid && onInvalid();
|
|
105
|
+
}
|
|
90
106
|
}}
|
|
91
|
-
error={error}
|
|
107
|
+
error={error || errorRequired}
|
|
92
108
|
textColor={textColor}
|
|
93
109
|
style={[styleInput['input-label'], style ?? []]}
|
|
94
110
|
contentStyle={[
|
|
95
111
|
styleInput['input-label'],
|
|
96
112
|
isFocus && isValid && styleInput['input-text--focused'],
|
|
97
|
-
!isValid && value
|
|
98
|
-
!isValid && !isFocus && value
|
|
113
|
+
!isValid && value?.trim() !== '' && styleInput['input-error-focused'],
|
|
114
|
+
!isValid && !isFocus && value?.trim() !== '' && styleInput['input-txt-error'],
|
|
115
|
+
errorRequired && styleInput['input-txt-error'],
|
|
116
|
+
errorRequired && isFocus && styleInput['input-error-focused'],
|
|
99
117
|
style ?? [],
|
|
100
118
|
]}
|
|
101
119
|
underlineColor={
|
|
102
120
|
underlineColor
|
|
103
121
|
? underlineColor
|
|
104
|
-
: isValid
|
|
105
|
-
|
|
106
|
-
|
|
122
|
+
: isValid && !errorRequired
|
|
123
|
+
? styleInput['input-text-underline'].color
|
|
124
|
+
: styleInput['input-txt-error'].borderBottomColor
|
|
107
125
|
}
|
|
108
126
|
activeUnderlineColor={
|
|
109
127
|
activeUnderlineColor
|
|
110
128
|
? activeUnderlineColor
|
|
111
|
-
: !isValid && value
|
|
112
|
-
|
|
113
|
-
|
|
129
|
+
: !isValid && value?.trim() !== ''
|
|
130
|
+
? styleInput['input-text--activeUnderline'].color
|
|
131
|
+
: errorRequired ? styleInput['input-txt-error'].color:''
|
|
114
132
|
}
|
|
115
133
|
keyboardType={type === 'number' ? 'numeric' : 'default'}
|
|
116
134
|
/>
|
|
117
|
-
{isValid && (
|
|
135
|
+
{isValid && !errorRequired && (
|
|
118
136
|
<HelperText type="info" style={styleInput['input-password-helper']} visible={true}>
|
|
119
137
|
{msgHelper}
|
|
120
138
|
</HelperText>
|
|
121
139
|
)}
|
|
122
|
-
{!isValid && msgError !== undefined && value
|
|
140
|
+
{!isValid && msgError !== undefined && value?.trim() !== '' && !errorRequired && (
|
|
123
141
|
<View style={styleInput['input-helper--error-text']}>
|
|
124
142
|
<Image style={styleInput['input-helper--error-image']} source={icons.error} />
|
|
125
143
|
<Text style={styleInput['input-password-helper--error-text']}>{msgError}</Text>
|
|
126
144
|
</View>
|
|
127
145
|
)}
|
|
146
|
+
{errorRequired && (
|
|
147
|
+
<View style={styleInput['input-helper--error-text']}>
|
|
148
|
+
<Image style={styleInput['input-helper--error-image']} source={icons.error} />
|
|
149
|
+
<Text style={styleInput['input-password-helper--error-text']}>{msgErrorRequired}</Text>
|
|
150
|
+
</View>
|
|
151
|
+
)}
|
|
128
152
|
</View>
|
|
129
153
|
);
|
|
130
154
|
};
|
|
@@ -25,6 +25,7 @@ interface IfInputPassword {
|
|
|
25
25
|
msgHelper?: string;
|
|
26
26
|
msgError?: string;
|
|
27
27
|
pattern?: RegExp;
|
|
28
|
+
readonly?: boolean;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export const InputPassword = (props: IfInputPassword) => {
|
|
@@ -49,6 +50,7 @@ export const InputPassword = (props: IfInputPassword) => {
|
|
|
49
50
|
msgError,
|
|
50
51
|
pattern,
|
|
51
52
|
onInvalid,
|
|
53
|
+
readonly
|
|
52
54
|
} = props;
|
|
53
55
|
const patternPasswordDefault = pattern ?? /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d#?!@$%^&*-]{8,}$/;
|
|
54
56
|
|
|
@@ -67,6 +69,7 @@ export const InputPassword = (props: IfInputPassword) => {
|
|
|
67
69
|
disabled={isDisabled}
|
|
68
70
|
value={value}
|
|
69
71
|
onChangeText={validateText}
|
|
72
|
+
readOnly={readonly}
|
|
70
73
|
onFocus={() => {
|
|
71
74
|
setIsFocus(true);
|
|
72
75
|
onFocus && onFocus();
|