aport-tools 4.5.0 → 4.6.0
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/package.json +17 -34
- package/src/cards/Card.tsx +129 -0
- package/src/cards/index.ts +1 -0
- package/src/components/Button.tsx +180 -0
- package/src/fonts/Text.tsx +137 -0
- package/{dist/fonts/index.d.ts → src/fonts/index.ts} +1 -0
- package/src/forms/ErrorList.tsx +47 -0
- package/src/forms/FORMDOC.md +87 -0
- package/{dist/forms/Form.d.ts → src/forms/Form.tsx} +2 -0
- package/src/forms/FormContext.tsx +248 -0
- package/src/forms/Input.tsx +174 -0
- package/src/forms/InputAttach.tsx +184 -0
- package/src/forms/InputCheck.tsx +169 -0
- package/src/forms/InputList.tsx +304 -0
- package/src/forms/Label.tsx +26 -0
- package/src/forms/Stepper.tsx +289 -0
- package/src/forms/TextArea.tsx +91 -0
- package/{dist/forms/index.d.ts → src/forms/index.ts} +4 -2
- package/src/index.ts +6 -0
- package/dist/cards/Card.d.ts +0 -57
- package/dist/cards/index.d.ts +0 -1
- package/dist/components/Button.d.ts +0 -52
- package/dist/defaults/reanimatedWrapper.d.ts +0 -2
- package/dist/fonts/Text.d.ts +0 -64
- package/dist/forms/ErrorList.d.ts +0 -6
- package/dist/forms/FormContext.d.ts +0 -132
- package/dist/forms/Input.d.ts +0 -43
- package/dist/forms/InputAttach.d.ts +0 -16
- package/dist/forms/InputCheck.d.ts +0 -19
- package/dist/forms/InputList.d.ts +0 -93
- package/dist/forms/Label.d.ts +0 -7
- package/dist/forms/Stepper.d.ts +0 -54
- package/dist/forms/TextArea.d.ts +0 -13
- package/dist/index.d.ts +0 -4
- package/dist/index.esm.js +0 -1493
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js +0 -1526
- package/dist/index.js.map +0 -1
- /package/{dist/buttons/index.d.ts → src/buttons/index.ts} +0 -0
package/package.json
CHANGED
@@ -1,28 +1,25 @@
|
|
1
1
|
{
|
2
2
|
"name": "aport-tools",
|
3
|
-
"version": "4.
|
3
|
+
"version": "4.6.0",
|
4
4
|
"description": "Aport mobile Tools with modern and minimalistic design",
|
5
|
-
"main": "
|
6
|
-
"
|
7
|
-
"react-native": "dist/index.js",
|
8
|
-
"source": "src/index.ts",
|
9
|
-
"types": "dist/index.d.ts",
|
5
|
+
"main": "src/index.ts",
|
6
|
+
"types": "src/index.ts",
|
10
7
|
"type": "module",
|
11
8
|
"exports": {
|
12
|
-
".":
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
"
|
17
|
-
"./forms": "./dist/forms/index.js"
|
9
|
+
".": {
|
10
|
+
"import": "./src/index.ts",
|
11
|
+
"types": "./src/index.ts"
|
12
|
+
},
|
13
|
+
"./*": "./src/*"
|
18
14
|
},
|
19
15
|
"files": [
|
20
|
-
"
|
16
|
+
"src"
|
21
17
|
],
|
22
18
|
"scripts": {
|
23
|
-
"build": "
|
19
|
+
"build": "tsc --noEmit",
|
24
20
|
"prepare": "npm run build",
|
25
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
22
|
+
"clean": "rm -rf dist .turbo node_modules/.cache .parcel-cache .expo .next .output .yalc .npm _site"
|
26
23
|
},
|
27
24
|
"keywords": [
|
28
25
|
"react-native",
|
@@ -36,30 +33,16 @@
|
|
36
33
|
"author": "EvansGxz",
|
37
34
|
"license": "ISC",
|
38
35
|
"devDependencies": {
|
39
|
-
"@
|
40
|
-
"@
|
41
|
-
"@babel/preset-react": "^7.24.7",
|
42
|
-
"@babel/preset-typescript": "^7.24.7",
|
43
|
-
"@rollup/plugin-babel": "^6.0.4",
|
44
|
-
"@rollup/plugin-commonjs": "^28.0.0",
|
45
|
-
"@rollup/plugin-json": "^6.1.0",
|
46
|
-
"@rollup/plugin-node-resolve": "^15.3.0",
|
47
|
-
"@rollup/plugin-typescript": "^12.1.0",
|
48
|
-
"@types/react": "^18.3.10",
|
49
|
-
"@types/react-native": "^0.73.0",
|
50
|
-
"react-native-reanimated": "^3.10.1",
|
51
|
-
"rollup": "^4.22.5",
|
52
|
-
"rollup-plugin-peer-deps-external": "^2.2.4",
|
36
|
+
"@types/react": "19.0.0",
|
37
|
+
"@types/react-native": "^0.79.0",
|
53
38
|
"typescript": "^5.6.2"
|
54
39
|
},
|
55
40
|
"peerDependencies": {
|
56
41
|
"@react-native-async-storage/async-storage": "^1.23.1",
|
57
42
|
"aport-themes": "^1.0.6",
|
58
43
|
"expo-image-picker": "^16.0.2",
|
59
|
-
"react": "
|
60
|
-
"react-native": "
|
61
|
-
"react-native-reanimated": "^3.
|
62
|
-
},
|
63
|
-
"dependencies": {
|
44
|
+
"react": "19.0.0",
|
45
|
+
"react-native": "0.79.0",
|
46
|
+
"react-native-reanimated": "^3.17.5"
|
64
47
|
}
|
65
48
|
}
|
@@ -0,0 +1,129 @@
|
|
1
|
+
// src/cards/Card.tsx
|
2
|
+
|
3
|
+
import { ThemeContext } from 'aport-themes';
|
4
|
+
import React, { useContext } from 'react';
|
5
|
+
import {
|
6
|
+
View,
|
7
|
+
StyleSheet,
|
8
|
+
StyleProp,
|
9
|
+
ViewStyle,
|
10
|
+
Platform,
|
11
|
+
TouchableOpacity,
|
12
|
+
GestureResponderEvent,
|
13
|
+
} from 'react-native';
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Interface for the props that the Card component accepts.
|
17
|
+
*/
|
18
|
+
interface CardProps {
|
19
|
+
/**
|
20
|
+
* Content to be rendered inside the Card.
|
21
|
+
*/
|
22
|
+
children: React.ReactNode;
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Style to be applied to the Card container.
|
26
|
+
*/
|
27
|
+
style?: StyleProp<ViewStyle>;
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Function to call when the Card is pressed.
|
31
|
+
*/
|
32
|
+
onPress?: (event: GestureResponderEvent) => void;
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Whether the Card is pressable. Defaults to false.
|
36
|
+
*/
|
37
|
+
pressable?: boolean;
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Border radius of the Card. Defaults to 12.
|
41
|
+
*/
|
42
|
+
borderRadius?: number;
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Elevation of the Card (Android only).
|
46
|
+
*/
|
47
|
+
elevation?: number;
|
48
|
+
|
49
|
+
/**
|
50
|
+
* Shadow properties for iOS.
|
51
|
+
*/
|
52
|
+
shadowProps?: {
|
53
|
+
shadowColor?: string;
|
54
|
+
shadowOffset?: { width: number; height: number };
|
55
|
+
shadowOpacity?: number;
|
56
|
+
shadowRadius?: number;
|
57
|
+
};
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Card component that adapts its styles based on the current theme.
|
62
|
+
* Supports dynamic styling, shadows, and press animations.
|
63
|
+
*
|
64
|
+
* @param children - The content to be displayed inside the Card.
|
65
|
+
* @param style - Additional styles to apply to the Card.
|
66
|
+
* @param onPress - Function to execute when the Card is pressed.
|
67
|
+
* @param pressable - Determines if the Card is pressable. Defaults to false.
|
68
|
+
* @param borderRadius - Border radius of the Card. Defaults to 12.
|
69
|
+
* @param elevation - Elevation for Android shadow. Overrides default.
|
70
|
+
* @param shadowProps - Custom shadow properties for iOS. Overrides defaults.
|
71
|
+
*/
|
72
|
+
export const Card: React.FC<CardProps> = ({
|
73
|
+
children,
|
74
|
+
style,
|
75
|
+
onPress,
|
76
|
+
pressable = false,
|
77
|
+
borderRadius = 12,
|
78
|
+
elevation = 4,
|
79
|
+
shadowProps = {},
|
80
|
+
}) => {
|
81
|
+
const { theme } = useContext(ThemeContext);
|
82
|
+
const { colors } = theme;
|
83
|
+
|
84
|
+
// Animation state for pressable effect
|
85
|
+
// Default shadow styles (improved platform-specific handling)
|
86
|
+
const defaultShadow = Platform.select({
|
87
|
+
ios: {
|
88
|
+
shadowColor: shadowProps?.shadowColor || colors.text.hex, // Defaulting to theme text color
|
89
|
+
shadowOffset: shadowProps?.shadowOffset || { width: 0, height: 2 },
|
90
|
+
shadowOpacity: shadowProps?.shadowOpacity || 0.1,
|
91
|
+
shadowRadius: shadowProps?.shadowRadius || 4,
|
92
|
+
},
|
93
|
+
android: {
|
94
|
+
elevation: elevation, // Only applies to Android
|
95
|
+
},
|
96
|
+
});
|
97
|
+
|
98
|
+
const cardStyles = [
|
99
|
+
styles.container,
|
100
|
+
{ borderRadius, backgroundColor: colors.background.hex },
|
101
|
+
defaultShadow, // Dynamic shadows based on platform
|
102
|
+
style, // External styles
|
103
|
+
];
|
104
|
+
|
105
|
+
return pressable ? (
|
106
|
+
<TouchableOpacity
|
107
|
+
activeOpacity={0.8}
|
108
|
+
onPress={onPress}
|
109
|
+
|
110
|
+
style={cardStyles}
|
111
|
+
>
|
112
|
+
|
113
|
+
{children}
|
114
|
+
</TouchableOpacity>
|
115
|
+
) : (
|
116
|
+
<View style={cardStyles}>
|
117
|
+
{children}
|
118
|
+
</View>
|
119
|
+
);
|
120
|
+
};
|
121
|
+
|
122
|
+
const styles = StyleSheet.create({
|
123
|
+
container: {
|
124
|
+
padding: 16,
|
125
|
+
borderRadius: 12,
|
126
|
+
// Shadows handled dynamically with platform logic
|
127
|
+
},
|
128
|
+
});
|
129
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
export { Card } from './Card';
|
@@ -0,0 +1,180 @@
|
|
1
|
+
// src/components/Button.tsx
|
2
|
+
|
3
|
+
import React, { useMemo, useContext } from 'react';
|
4
|
+
import { Text, ViewStyle, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native';
|
5
|
+
import { useFormContext } from '../forms';
|
6
|
+
import { ThemeColors, ThemeContext } from 'aport-themes';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Interface for the props that the Button component accepts.
|
10
|
+
*/
|
11
|
+
interface ButtonProps {
|
12
|
+
/**
|
13
|
+
* If true, the button is disabled and not pressable.
|
14
|
+
*/
|
15
|
+
disabled?: boolean;
|
16
|
+
|
17
|
+
/**
|
18
|
+
* If true, the button expands to full width of its container.
|
19
|
+
*/
|
20
|
+
isFullWidth?: boolean;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Text content of the button.
|
24
|
+
*/
|
25
|
+
children?: string;
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Function to call when the button is pressed.
|
29
|
+
*/
|
30
|
+
onPress?: () => void;
|
31
|
+
|
32
|
+
/**
|
33
|
+
* If true, the button has rounded corners.
|
34
|
+
*/
|
35
|
+
rounded?: boolean;
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Custom border radius value. Overrides the `rounded` prop if provided.
|
39
|
+
*/
|
40
|
+
borderRadius?: number;
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Specifies the button type for styling. Can be 'submit', 'button', or 'cancel'.
|
44
|
+
*/
|
45
|
+
type?: 'submit' | 'button' | 'cancel';
|
46
|
+
|
47
|
+
/**
|
48
|
+
* If true, a loading spinner is shown and the button is disabled.
|
49
|
+
*/
|
50
|
+
loading?: boolean;
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Determines the styles based on the button type and whether it is disabled.
|
55
|
+
*
|
56
|
+
* @param type - The type of the button ('submit', 'button', 'cancel').
|
57
|
+
* @param disabled - Whether the button is disabled.
|
58
|
+
* @param themeColors - The theme colors.
|
59
|
+
* @returns The computed style for the button.
|
60
|
+
*/
|
61
|
+
function typeStyles(
|
62
|
+
type?: string,
|
63
|
+
disabled?: boolean,
|
64
|
+
themeColors?: ThemeColors
|
65
|
+
): ViewStyle {
|
66
|
+
switch (type) {
|
67
|
+
case 'submit':
|
68
|
+
return {
|
69
|
+
backgroundColor: `rgba(${themeColors?.primary.rgb.r}, ${themeColors?.primary.rgb.g}, ${themeColors?.primary.rgb.b}, ${
|
70
|
+
disabled ? 0.5 : 1
|
71
|
+
})`,
|
72
|
+
borderWidth: 2,
|
73
|
+
borderColor: themeColors?.primary.hex,
|
74
|
+
};
|
75
|
+
case 'button':
|
76
|
+
return {
|
77
|
+
backgroundColor: themeColors?.primary.hex,
|
78
|
+
borderColor: themeColors?.secondary.hex,
|
79
|
+
opacity: disabled ? 0.5 : 1,
|
80
|
+
borderWidth: 2,
|
81
|
+
};
|
82
|
+
case 'cancel':
|
83
|
+
return {
|
84
|
+
backgroundColor: themeColors?.background.hex,
|
85
|
+
borderWidth: 0,
|
86
|
+
};
|
87
|
+
default:
|
88
|
+
return {};
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Button component that adapts its styles based on the current theme.
|
94
|
+
* Supports dynamic styling, full-width option, rounded corners, and different types.
|
95
|
+
*
|
96
|
+
* @param disabled - If true, the button is disabled and not pressable.
|
97
|
+
* @param isFullWidth - If true, the button expands to full width of its container.
|
98
|
+
* @param children - Text content of the button.
|
99
|
+
* @param onPress - Function to call when the button is pressed.
|
100
|
+
* @param rounded - If true, the button has rounded corners.
|
101
|
+
* @param borderRadius - Custom border radius value. Overrides the `rounded` prop if provided.
|
102
|
+
* @param type - Specifies the button type for styling ('submit', 'button', 'cancel').
|
103
|
+
*/
|
104
|
+
export const Button: React.FC<ButtonProps> = ({
|
105
|
+
children,
|
106
|
+
disabled = false,
|
107
|
+
type = 'button',
|
108
|
+
rounded = true,
|
109
|
+
borderRadius = 30,
|
110
|
+
isFullWidth = false,
|
111
|
+
loading = false,
|
112
|
+
onPress,
|
113
|
+
}) => {
|
114
|
+
const { theme } = useContext(ThemeContext);
|
115
|
+
const { colors } = theme;
|
116
|
+
|
117
|
+
const computedStyles = useMemo(() => {
|
118
|
+
return StyleSheet.flatten([
|
119
|
+
styles.button,
|
120
|
+
typeStyles(type, disabled, colors),
|
121
|
+
rounded && { borderRadius },
|
122
|
+
isFullWidth && { width: '100%' },
|
123
|
+
(disabled || loading) && styles.disabled,
|
124
|
+
]);
|
125
|
+
}, [type, disabled, loading, rounded, borderRadius, isFullWidth, colors]);
|
126
|
+
|
127
|
+
const textColor = useMemo(() => {
|
128
|
+
return type === "cancel" ? { color: colors.text.hex } : { color: colors.textButton.hex };
|
129
|
+
}, [type, colors]);
|
130
|
+
|
131
|
+
// Safely try to access handleSubmit from the form context
|
132
|
+
const formContext = (() => {
|
133
|
+
try {
|
134
|
+
return useFormContext();
|
135
|
+
} catch {
|
136
|
+
return null;
|
137
|
+
}
|
138
|
+
})();
|
139
|
+
|
140
|
+
const handlePress = () => {
|
141
|
+
if (type === 'submit' && formContext?.handleSubmit) {
|
142
|
+
console.log("Its submit type");
|
143
|
+
|
144
|
+
formContext.handleSubmit();
|
145
|
+
} else if (onPress) {
|
146
|
+
onPress();
|
147
|
+
}
|
148
|
+
};
|
149
|
+
|
150
|
+
|
151
|
+
return (
|
152
|
+
<TouchableOpacity
|
153
|
+
style={computedStyles as ViewStyle}
|
154
|
+
disabled={disabled || loading}
|
155
|
+
onPress={handlePress}
|
156
|
+
activeOpacity={0.7}
|
157
|
+
>
|
158
|
+
{loading ? ( // Show loading spinner if loading
|
159
|
+
<ActivityIndicator size="small" color={colors.textButton.hex} />
|
160
|
+
) : (
|
161
|
+
<Text style={textColor}>
|
162
|
+
{Array.isArray(children) ? children.join('').toUpperCase() : children?.toUpperCase()}
|
163
|
+
</Text>
|
164
|
+
)}
|
165
|
+
</TouchableOpacity>
|
166
|
+
);
|
167
|
+
};
|
168
|
+
|
169
|
+
const styles = StyleSheet.create({
|
170
|
+
button: {
|
171
|
+
justifyContent: 'center',
|
172
|
+
alignItems: 'center',
|
173
|
+
paddingVertical: 10,
|
174
|
+
paddingHorizontal: 20,
|
175
|
+
},
|
176
|
+
disabled: {
|
177
|
+
opacity: 0.6,
|
178
|
+
},
|
179
|
+
});
|
180
|
+
|
@@ -0,0 +1,137 @@
|
|
1
|
+
// src/fonts/Text.tsx
|
2
|
+
|
3
|
+
import { ThemeColors, ThemeContext, ThemeFonts } from 'aport-themes';
|
4
|
+
import React, { useContext } from 'react';
|
5
|
+
import { Text as RNText, StyleSheet, TextProps, TextStyle } from 'react-native';
|
6
|
+
|
7
|
+
interface CustomTextProps extends TextProps {
|
8
|
+
/**
|
9
|
+
* Content to be displayed inside the Text component.
|
10
|
+
*/
|
11
|
+
children: React.ReactNode;
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Size of the text. Defaults to 14.
|
15
|
+
*/
|
16
|
+
size?: number;
|
17
|
+
|
18
|
+
/**
|
19
|
+
* If true, applies bold styling to the text.
|
20
|
+
*/
|
21
|
+
b?: boolean;
|
22
|
+
|
23
|
+
/**
|
24
|
+
* If true, applies italic styling to the text.
|
25
|
+
*/
|
26
|
+
i?: boolean;
|
27
|
+
|
28
|
+
/**
|
29
|
+
* If true, applies underline styling to the text.
|
30
|
+
*/
|
31
|
+
u?: boolean;
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Paragraph alignment. Can be 'left', 'center', or 'right'. Defaults to 'left'.
|
35
|
+
*/
|
36
|
+
align?: 'left' | 'center' | 'right' | 'justify'; // Paragraph alignment
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Text type. Defaults to 'text'. Should match the keys in ThemeColors.
|
40
|
+
* This restricts type to known color keys like 'primary', 'error', etc.
|
41
|
+
*/
|
42
|
+
type?: keyof ThemeColors;
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Text font type. Defaults to 'body'. Should match the keys in ThemeFonts.
|
46
|
+
* This restricts type to known fonts keys like 'primary', 'secundary', etc.
|
47
|
+
*/
|
48
|
+
typeFont?: keyof ThemeFonts; // <-- New prop to select font type
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Header level. Accepts 0 (none), 1, 2, or 3. Defaults to 0.
|
52
|
+
*/
|
53
|
+
h?: 0 | 1 | 2 | 3;
|
54
|
+
|
55
|
+
/**
|
56
|
+
* Additional styles to apply to the Text component.
|
57
|
+
*/
|
58
|
+
style?: TextStyle | TextStyle[];
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* A dynamic Text component that supports HTML-like formatting.
|
63
|
+
* Integrates with the Theme system for consistent styling.
|
64
|
+
*
|
65
|
+
* @param children - The content to be displayed inside the Text component.
|
66
|
+
* @param size - Size of the text. Defaults to 'medium'.
|
67
|
+
* @param b - If true, applies bold styling.
|
68
|
+
* @param i - If true, applies italic styling.
|
69
|
+
* @param type - Type of Text component. Defaults to 'text'.
|
70
|
+
* @param typeFont - Type of Font component. Defaults to 'body'.
|
71
|
+
* @param u - If true, applies underline styling.
|
72
|
+
* @param align - Paragraph alignment. Defaults to 'left'.
|
73
|
+
* @param h - Header level. Accepts 0 (none), 1, 2, or 3. Defaults to 0.
|
74
|
+
* @param style - Additional styles to apply.
|
75
|
+
*/
|
76
|
+
export const Text: React.FC<CustomTextProps> = ({
|
77
|
+
children,
|
78
|
+
size = 14,
|
79
|
+
b = false,
|
80
|
+
i = false,
|
81
|
+
u = false,
|
82
|
+
align = 'left',
|
83
|
+
type = 'text',
|
84
|
+
typeFont = 'body',
|
85
|
+
h = 0,
|
86
|
+
style,
|
87
|
+
}) => {
|
88
|
+
const { theme } = useContext(ThemeContext);
|
89
|
+
const { colors, fonts } = theme;
|
90
|
+
|
91
|
+
// Determine font family and size based on 'typeFont'
|
92
|
+
const fontSettings = fonts[typeFont] || fonts.body;
|
93
|
+
const fontFamily = fontSettings.fontFamily;
|
94
|
+
let fontSize = fontSettings.fontSize || size;
|
95
|
+
|
96
|
+
// Calculate header size based on 'h' prop
|
97
|
+
if (h === 1) fontSize = 32;
|
98
|
+
else if (h === 2) fontSize = 24;
|
99
|
+
else if (h === 3) fontSize = 18;
|
100
|
+
|
101
|
+
// Define font weight based on bold prop
|
102
|
+
const fontWeight = b ? '700' : fontSettings.fontWeight || '400';
|
103
|
+
|
104
|
+
// Define font style based on italic prop
|
105
|
+
const fontStyle = i ? 'italic' : 'normal';
|
106
|
+
|
107
|
+
// Define text decoration based on underline prop
|
108
|
+
const textDecorationLine = u ? 'underline' : 'none';
|
109
|
+
|
110
|
+
// Define text alignment
|
111
|
+
const textAlign: TextStyle['textAlign'] = align;
|
112
|
+
// Map the 'type' prop to the correct color from the theme
|
113
|
+
const textColor = colors[type]?.hex || colors.text.hex;
|
114
|
+
|
115
|
+
// Combine all styles
|
116
|
+
const textStyles: TextStyle = {
|
117
|
+
fontSize,
|
118
|
+
fontWeight,
|
119
|
+
fontFamily,
|
120
|
+
fontStyle,
|
121
|
+
textDecorationLine,
|
122
|
+
textAlign,
|
123
|
+
color: textColor,
|
124
|
+
};
|
125
|
+
|
126
|
+
|
127
|
+
return (
|
128
|
+
<RNText style={[textStyles, style]}>
|
129
|
+
{children}
|
130
|
+
</RNText>
|
131
|
+
);
|
132
|
+
};
|
133
|
+
|
134
|
+
const styles = StyleSheet.create({
|
135
|
+
// Define any default styles if needed
|
136
|
+
});
|
137
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
// src/forms/ErrorList.tsx
|
2
|
+
|
3
|
+
import React, { useContext } from 'react';
|
4
|
+
import { View, StyleSheet } from 'react-native';
|
5
|
+
import { Text } from '../fonts/Text';
|
6
|
+
import { ThemeContext } from 'aport-themes';
|
7
|
+
|
8
|
+
interface ErrorListProps {
|
9
|
+
errors: string[];
|
10
|
+
}
|
11
|
+
|
12
|
+
const ErrorList: React.FC<ErrorListProps> = ({ errors }) => {
|
13
|
+
const { theme } = useContext(ThemeContext);
|
14
|
+
const { colors } = theme;
|
15
|
+
|
16
|
+
return (
|
17
|
+
<View style={styles.container}>
|
18
|
+
{errors.map((error, index) => (
|
19
|
+
<View key={index} style={styles.errorItem}>
|
20
|
+
<Text style={[styles.bullet, { color: colors.error.hex }]}>•</Text>
|
21
|
+
<Text style={[styles.errorText, { color: colors.error.hex }]}>{error}</Text>
|
22
|
+
</View>
|
23
|
+
))}
|
24
|
+
</View>
|
25
|
+
);
|
26
|
+
};
|
27
|
+
|
28
|
+
const styles = StyleSheet.create({
|
29
|
+
container: {
|
30
|
+
marginTop: 4,
|
31
|
+
},
|
32
|
+
errorItem: {
|
33
|
+
flexDirection: 'row',
|
34
|
+
alignItems: 'flex-start',
|
35
|
+
marginBottom: 2,
|
36
|
+
},
|
37
|
+
bullet: {
|
38
|
+
marginRight: 4,
|
39
|
+
fontSize: 12,
|
40
|
+
},
|
41
|
+
errorText: {
|
42
|
+
flex: 1,
|
43
|
+
fontSize: 12,
|
44
|
+
},
|
45
|
+
});
|
46
|
+
|
47
|
+
export default ErrorList;
|
@@ -0,0 +1,87 @@
|
|
1
|
+
Input Component Documentation
|
2
|
+
File Path
|
3
|
+
src/forms/Input.tsx
|
4
|
+
|
5
|
+
Description
|
6
|
+
The Input component is a versatile and reusable form input field designed to handle various input types with built-in validation and error display functionalities. It integrates seamlessly with the theme system to adapt its styles based on the current theme (light or dark). The component supports automatic formatting for specific input types such as phone numbers and identification fields, enhancing user experience and ensuring data consistency.
|
7
|
+
|
8
|
+
Component Overview
|
9
|
+
Labels: Displays a label above the input field.
|
10
|
+
Error Handling: Shows a list of error messages below the input field when validation fails.
|
11
|
+
Input Types: Supports different input formats (phone, id, uppercase, numeric) with automatic formatting.
|
12
|
+
Theming: Adapts styles based on the active theme, utilizing the body color for backgrounds in dark mode.
|
13
|
+
Controlled Component: Manages its state through the FormContext, allowing centralized form management.
|
14
|
+
Props
|
15
|
+
Prop Type Required Description
|
16
|
+
name string Yes The unique identifier for the input field, used to manage its state within the form.
|
17
|
+
label string Yes The text label displayed above the input field.
|
18
|
+
inputType 'phone' | 'id' | 'uppercase' | 'numeric' No Specifies the type of input and applies corresponding formatting. Defaults to standard text input.
|
19
|
+
style TextInputProps['style'] No Additional styles to customize the appearance of the TextInput component.
|
20
|
+
...rest TextInputProps No Spread any additional TextInput props to allow further customization and flexibility.
|
21
|
+
|
22
|
+
Supported inputType Values
|
23
|
+
phone: Formats the input as a phone number (xxx-xxx-xxxx), automatically inserting hyphens.
|
24
|
+
id: Converts all input characters to uppercase, suitable for identification numbers like IDs, passports, and visas.
|
25
|
+
uppercase: Forces all input characters to uppercase, useful for fields like license plates or certain codes.
|
26
|
+
numeric: Restricts input to numeric characters only, ideal for fields like ZIP codes, ages, or any numerical data.
|
27
|
+
Usage Example
|
28
|
+
tsx
|
29
|
+
Copy code
|
30
|
+
// ExampleUsage.tsx
|
31
|
+
|
32
|
+
import React from 'react';
|
33
|
+
import { ScrollView, View } from 'react-native';
|
34
|
+
import { Form } from './forms/FormContext';
|
35
|
+
import { Input } from './forms/Input';
|
36
|
+
import { Button } from '../components/Button';
|
37
|
+
|
38
|
+
const MyFormScreen: React.FC = () => {
|
39
|
+
const handleFormSubmit = async (formValues: Record<string, any>) => {
|
40
|
+
const errors: Record<string, string[]> = {};
|
41
|
+
|
42
|
+
// Validate the 'name' field
|
43
|
+
if (!formValues.name) {
|
44
|
+
errors.name = ['Name is required'];
|
45
|
+
}
|
46
|
+
|
47
|
+
// Validate the 'email' field
|
48
|
+
if (!formValues.email) {
|
49
|
+
errors.email = ['Email is required'];
|
50
|
+
} else {
|
51
|
+
if (formValues.email.length < 4) {
|
52
|
+
errors.email = [...(errors.email || []), 'Email is too short'];
|
53
|
+
}
|
54
|
+
if (!formValues.email.includes('@')) {
|
55
|
+
errors.email = [...(errors.email || []), 'Email must include @'];
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
// Validate the 'phone' field
|
60
|
+
if (!formValues.phone) {
|
61
|
+
errors.phone = ['Phone is required'];
|
62
|
+
}
|
63
|
+
|
64
|
+
// Return validation errors if any exist
|
65
|
+
return errors;
|
66
|
+
|
67
|
+
};
|
68
|
+
|
69
|
+
return (
|
70
|
+
<ScrollView>
|
71
|
+
<Form onSubmit={handleFormSubmit}>
|
72
|
+
<View style={{ padding: 16 }}>
|
73
|
+
<Input name="name" label="First Name" inputType="uppercase" />
|
74
|
+
<Input name="lastName" label="Last Name" inputType="uppercase" />
|
75
|
+
<Input name="email" label="Email" keyboardType="email-address" inputType="id" />
|
76
|
+
<Input name="phone" label="Phone" keyboardType="number-pad" maxLength={12} inputType="phone" />
|
77
|
+
|
78
|
+
{/* Form submit button */}
|
79
|
+
<Button type="submit">Submit</Button>
|
80
|
+
</View>
|
81
|
+
</Form>
|
82
|
+
</ScrollView>
|
83
|
+
|
84
|
+
);
|
85
|
+
};
|
86
|
+
|
87
|
+
export default MyFormScreen;
|