@teamnhz/rn-ui-toolkit 1.0.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/dist/assets/images/cancel.png +0 -0
- package/dist/assets/images/eye_off.png +0 -0
- package/dist/assets/images/eye_on.png +0 -0
- package/dist/assets/images/favourite.png +0 -0
- package/dist/assets/images/image_icon.png +0 -0
- package/dist/assets/images/upload.png +0 -0
- package/dist/assets/images/video_icon.png +0 -0
- package/dist/components/Accordion/index.d.ts +15 -0
- package/dist/components/Accordion/index.js +51 -0
- package/dist/components/BottomSheet/index.d.ts +10 -0
- package/dist/components/BottomSheet/index.js +35 -0
- package/dist/components/Buttons/index.d.ts +14 -0
- package/dist/components/Buttons/index.js +46 -0
- package/dist/components/CheckBox/index.d.ts +21 -0
- package/dist/components/CheckBox/index.js +42 -0
- package/dist/components/Container/index.d.ts +11 -0
- package/dist/components/Container/index.js +30 -0
- package/dist/components/DateTimePicker/index.d.ts +11 -0
- package/dist/components/DateTimePicker/index.js +50 -0
- package/dist/components/Dividers/index.d.ts +9 -0
- package/dist/components/Dividers/index.js +23 -0
- package/dist/components/DocumentPicker/index.d.ts +16 -0
- package/dist/components/DocumentPicker/index.js +52 -0
- package/dist/components/DropDown/index.d.ts +24 -0
- package/dist/components/DropDown/index.js +34 -0
- package/dist/components/FullSpinner/index.d.ts +6 -0
- package/dist/components/FullSpinner/index.js +22 -0
- package/dist/components/HProgressSteps/index.d.ts +15 -0
- package/dist/components/HProgressSteps/index.js +102 -0
- package/dist/components/HStack/index.d.ts +10 -0
- package/dist/components/HStack/index.js +13 -0
- package/dist/components/HorizontalFlatList/index.d.ts +10 -0
- package/dist/components/HorizontalFlatList/index.js +14 -0
- package/dist/components/Image/index.d.ts +5 -0
- package/dist/components/Image/index.js +6 -0
- package/dist/components/ImagePicker/index.d.ts +10 -0
- package/dist/components/ImagePicker/index.js +109 -0
- package/dist/components/Input/index.d.ts +26 -0
- package/dist/components/Input/index.js +118 -0
- package/dist/components/Modal/index.d.ts +9 -0
- package/dist/components/Modal/index.js +5 -0
- package/dist/components/MyStatusBar/index.d.ts +8 -0
- package/dist/components/MyStatusBar/index.js +13 -0
- package/dist/components/ProgressBar/index.d.ts +9 -0
- package/dist/components/ProgressBar/index.js +26 -0
- package/dist/components/RadioButton/index.d.ts +18 -0
- package/dist/components/RadioButton/index.js +49 -0
- package/dist/components/ScrolledContainer/index.d.ts +13 -0
- package/dist/components/ScrolledContainer/index.js +30 -0
- package/dist/components/ShouldRender/index.d.ts +6 -0
- package/dist/components/ShouldRender/index.js +5 -0
- package/dist/components/Spinner/index.d.ts +8 -0
- package/dist/components/Spinner/index.js +17 -0
- package/dist/components/Switch/index.d.ts +6 -0
- package/dist/components/Switch/index.js +23 -0
- package/dist/components/T/index.d.ts +17 -0
- package/dist/components/T/index.js +20 -0
- package/dist/components/Toast/index.d.ts +13 -0
- package/dist/components/Toast/index.js +84 -0
- package/dist/components/VProgressSteps/index.d.ts +3 -0
- package/dist/components/VProgressSteps/index.js +77 -0
- package/dist/components/VStack/index.d.ts +9 -0
- package/dist/components/VStack/index.js +15 -0
- package/dist/components/VerticalFlatList/index.d.ts +7 -0
- package/dist/components/VerticalFlatList/index.js +6 -0
- package/dist/components/index.d.ts +28 -0
- package/dist/components/index.js +28 -0
- package/dist/constants/messages.d.ts +11 -0
- package/dist/constants/messages.js +11 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/styles/colors.d.ts +32 -0
- package/dist/styles/colors.js +31 -0
- package/dist/styles/config.json +108 -0
- package/dist/styles/images.d.ts +9 -0
- package/dist/styles/images.js +15 -0
- package/dist/styles/index.d.ts +7 -0
- package/dist/styles/index.js +9 -0
- package/dist/styles/mixins.d.ts +64 -0
- package/dist/styles/mixins.js +68 -0
- package/dist/styles/scale.d.ts +4 -0
- package/dist/styles/scale.js +9 -0
- package/dist/styles/typography.d.ts +89 -0
- package/dist/styles/typography.js +123 -0
- package/dist/utils/permissionMessage.d.ts +12 -0
- package/dist/utils/permissionMessage.js +12 -0
- package/dist/utils/permissions.d.ts +18 -0
- package/dist/utils/permissions.js +106 -0
- package/dist/utils/regex.d.ts +2 -0
- package/dist/utils/regex.js +7 -0
- package/package.json +37 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, Dimensions } from 'react-native';
|
|
3
|
+
const { width } = Dimensions.get('window');
|
|
4
|
+
// A placeholder for the Colors object, as it's not provided.
|
|
5
|
+
// You should replace this with your actual Colors import.
|
|
6
|
+
const Colors = {
|
|
7
|
+
primaryColor: '#4F80E1',
|
|
8
|
+
white: '#FFFFFF',
|
|
9
|
+
borderGrey: '#B0B0B0',
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* A reusable horizontal progress steps component for React Native.
|
|
13
|
+
* It displays a series of circles connected by lines to represent steps.
|
|
14
|
+
* The active and completed steps have different styling.
|
|
15
|
+
*
|
|
16
|
+
* @param {Array<string>} steps - An array of strings representing the step titles.
|
|
17
|
+
* @param {number} currentStep - The index of the currently active step (0-indexed).
|
|
18
|
+
* @param {string} [activeStepColor] - Color for the active step circle. Defaults to Colors.primaryColor.
|
|
19
|
+
* @param {string} [completedStepColor] - Color for completed steps. Defaults to Colors.primaryColor.
|
|
20
|
+
* @param {string} [defaultStepColor] - Color for incomplete steps. Defaults to Colors.borderGrey.
|
|
21
|
+
* @param {number} [stepSize=30] - Diameter of the step circles.
|
|
22
|
+
*/
|
|
23
|
+
const HProgressSteps = ({ steps, currentStep, activeStepColor = Colors.primaryColor, completedStepColor = Colors.primaryColor, defaultStepColor = Colors.borderGrey, stepSize = 30, }) => {
|
|
24
|
+
return (React.createElement(View, { style: styles.container }, steps.map((step, index) => {
|
|
25
|
+
// Determine the style for each step based on its index
|
|
26
|
+
const isCompleted = index < currentStep;
|
|
27
|
+
const isActive = index === currentStep;
|
|
28
|
+
const isLastStep = index === steps.length - 1;
|
|
29
|
+
// Apply dynamic styles for circles
|
|
30
|
+
const circleStyle = {
|
|
31
|
+
width: stepSize,
|
|
32
|
+
height: stepSize,
|
|
33
|
+
borderRadius: stepSize / 2,
|
|
34
|
+
backgroundColor: isCompleted ? completedStepColor : (isActive ? Colors.white : defaultStepColor),
|
|
35
|
+
justifyContent: 'center',
|
|
36
|
+
alignItems: 'center',
|
|
37
|
+
borderWidth: 2,
|
|
38
|
+
borderColor: isCompleted || isActive ? activeStepColor : defaultStepColor,
|
|
39
|
+
};
|
|
40
|
+
// Apply dynamic styles for lines
|
|
41
|
+
const lineStyle = {
|
|
42
|
+
flex: 1,
|
|
43
|
+
height: 2,
|
|
44
|
+
backgroundColor: isCompleted ? completedStepColor : defaultStepColor,
|
|
45
|
+
};
|
|
46
|
+
return (React.createElement(React.Fragment, { key: index },
|
|
47
|
+
React.createElement(View, { style: styles.stepContainer },
|
|
48
|
+
React.createElement(View, { style: [circleStyle] },
|
|
49
|
+
React.createElement(Text, { style: styles.stepText }, index + 1)),
|
|
50
|
+
React.createElement(Text, { style: [styles.label, isActive && styles.activeLabel] }, step)),
|
|
51
|
+
!isLastStep && React.createElement(View, { style: lineStyle })));
|
|
52
|
+
})));
|
|
53
|
+
};
|
|
54
|
+
export default HProgressSteps;
|
|
55
|
+
const styles = StyleSheet.create({
|
|
56
|
+
appContainer: {
|
|
57
|
+
flex: 1,
|
|
58
|
+
backgroundColor: '#fff',
|
|
59
|
+
alignItems: 'center',
|
|
60
|
+
justifyContent: 'flex-start',
|
|
61
|
+
paddingTop: 50,
|
|
62
|
+
},
|
|
63
|
+
header: {
|
|
64
|
+
fontSize: 24,
|
|
65
|
+
fontWeight: 'bold',
|
|
66
|
+
marginBottom: 40,
|
|
67
|
+
},
|
|
68
|
+
stepsWrapper: {
|
|
69
|
+
width: width * 0.9,
|
|
70
|
+
paddingHorizontal: 10,
|
|
71
|
+
marginBottom: 40,
|
|
72
|
+
},
|
|
73
|
+
container: {
|
|
74
|
+
flexDirection: 'row',
|
|
75
|
+
alignItems: 'center',
|
|
76
|
+
justifyContent: 'space-between',
|
|
77
|
+
},
|
|
78
|
+
stepContainer: {
|
|
79
|
+
alignItems: 'center',
|
|
80
|
+
justifyContent: 'center',
|
|
81
|
+
},
|
|
82
|
+
activeStepBorder: {
|
|
83
|
+
borderColor: '#4F80E1',
|
|
84
|
+
borderWidth: 2,
|
|
85
|
+
backgroundColor: 'white',
|
|
86
|
+
},
|
|
87
|
+
stepText: {
|
|
88
|
+
color: 'white',
|
|
89
|
+
fontSize: 16,
|
|
90
|
+
fontWeight: 'bold',
|
|
91
|
+
},
|
|
92
|
+
label: {
|
|
93
|
+
marginTop: 8,
|
|
94
|
+
textAlign: 'center',
|
|
95
|
+
fontSize: 14,
|
|
96
|
+
color: '#888',
|
|
97
|
+
},
|
|
98
|
+
activeLabel: {
|
|
99
|
+
fontWeight: 'bold',
|
|
100
|
+
color: Colors.primaryColor,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ViewStyle, FlexStyle } from 'react-native';
|
|
3
|
+
interface StackProps {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
spacing?: number;
|
|
6
|
+
style?: ViewStyle;
|
|
7
|
+
wrap?: FlexStyle['flexWrap'];
|
|
8
|
+
}
|
|
9
|
+
declare const HStack: React.FC<StackProps>;
|
|
10
|
+
export default HStack;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
const HStack = ({ children, spacing = 8, style, wrap = 'nowrap' }) => {
|
|
4
|
+
const validChildren = React.Children.toArray(children);
|
|
5
|
+
return (React.createElement(View, { style: [styles.row, { flexWrap: wrap }, style] }, validChildren.map((child, index) => (React.createElement(View, { key: index, style: { marginRight: index !== validChildren.length - 1 ? spacing : 0 } }, child)))));
|
|
6
|
+
};
|
|
7
|
+
export default HStack;
|
|
8
|
+
const styles = StyleSheet.create({
|
|
9
|
+
row: {
|
|
10
|
+
flexDirection: 'row',
|
|
11
|
+
alignItems: 'center',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FlatListProps } from "react-native";
|
|
3
|
+
interface HorizontalFlatListProps<ItemT> extends Omit<FlatListProps<ItemT>, "horizontal" | "renderItem"> {
|
|
4
|
+
spacing?: number;
|
|
5
|
+
renderItem: ({ item }: {
|
|
6
|
+
item: ItemT;
|
|
7
|
+
}) => React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
declare const HorizontalFlatList: <ItemT>({ spacing, renderItem, ...props }: HorizontalFlatListProps<ItemT>) => React.JSX.Element;
|
|
10
|
+
export default HorizontalFlatList;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FlatList, View, StyleSheet } from "react-native";
|
|
3
|
+
const HorizontalFlatList = ({ spacing = 10, renderItem, ...props }) => {
|
|
4
|
+
return (React.createElement(FlatList, { ...props, horizontal: true, showsHorizontalScrollIndicator: props.showsHorizontalScrollIndicator, keyExtractor: (item, index) => item.id?.toString() || index.toString(), renderItem: ({ item, index }) => (React.createElement(View, { style: [
|
|
5
|
+
styles.box,
|
|
6
|
+
{ marginRight: index === (props.data?.length ?? 0) - 1 ? 0 : spacing },
|
|
7
|
+
] }, renderItem({ item }))) }));
|
|
8
|
+
};
|
|
9
|
+
const styles = StyleSheet.create({
|
|
10
|
+
box: {
|
|
11
|
+
flex: 1
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
export default HorizontalFlatList;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type Props = {
|
|
3
|
+
mediaType: "photo" | "video";
|
|
4
|
+
isMultiSelect?: boolean;
|
|
5
|
+
onSuccess: (data: any) => void;
|
|
6
|
+
visible: boolean;
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
};
|
|
9
|
+
declare const ImagePicker: React.FC<Props>;
|
|
10
|
+
export default ImagePicker;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet, Image, Alert } from "react-native";
|
|
3
|
+
import { BottomSheet, Dividers } from "../index";
|
|
4
|
+
import { launchCamera, launchImageLibrary } from "react-native-image-picker";
|
|
5
|
+
import ImageCropPicker from "react-native-image-crop-picker";
|
|
6
|
+
import { Colors, Images, Scale, Typography } from "../../styles";
|
|
7
|
+
const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, }) => {
|
|
8
|
+
const onComplete = useCallback((data) => {
|
|
9
|
+
onSuccess(data);
|
|
10
|
+
onClose();
|
|
11
|
+
}, [onSuccess, onClose]);
|
|
12
|
+
const onCamera = () => {
|
|
13
|
+
if (mediaType === "photo") {
|
|
14
|
+
if (isMultiSelect) {
|
|
15
|
+
ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
16
|
+
.then((response) => onComplete([response]))
|
|
17
|
+
.catch(() => { });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
21
|
+
.then((response) => ImageCropPicker.openCropper({
|
|
22
|
+
path: response?.path,
|
|
23
|
+
width: response?.width,
|
|
24
|
+
height: response?.height,
|
|
25
|
+
mediaType: "photo",
|
|
26
|
+
freeStyleCropEnabled: true,
|
|
27
|
+
}).then(onComplete))
|
|
28
|
+
.catch(() => { });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
launchCamera({ mediaType: "video" }, (response) => {
|
|
33
|
+
if (response?.assets) {
|
|
34
|
+
onComplete({
|
|
35
|
+
path: response.assets[0]?.uri,
|
|
36
|
+
duration: response.assets[0]?.duration,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const onGallery = () => {
|
|
43
|
+
if (mediaType === "photo") {
|
|
44
|
+
if (isMultiSelect) {
|
|
45
|
+
ImageCropPicker.openPicker({ mediaType: "photo", multiple: true, maxFiles: 5 })
|
|
46
|
+
.then((images) => {
|
|
47
|
+
if (images.length > 5) {
|
|
48
|
+
Alert.alert("Limit Exceeded", "You can only select up to 5 images.");
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
onComplete(images);
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
.catch(() => { });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
ImageCropPicker.openPicker({ mediaType: "photo" })
|
|
58
|
+
.then((response) => ImageCropPicker.openCropper({
|
|
59
|
+
path: response?.path,
|
|
60
|
+
width: response?.width,
|
|
61
|
+
height: response?.height,
|
|
62
|
+
mediaType: "photo",
|
|
63
|
+
freeStyleCropEnabled: true,
|
|
64
|
+
}).then(onComplete))
|
|
65
|
+
.catch(() => { });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
launchImageLibrary({ mediaType: "video" }, (result) => {
|
|
70
|
+
if (result?.assets) {
|
|
71
|
+
onComplete({
|
|
72
|
+
path: result.assets[0]?.uri,
|
|
73
|
+
duration: result.assets[0]?.duration,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
return (React.createElement(BottomSheet, { visible: visible, onClose: onClose, height: 210 },
|
|
80
|
+
React.createElement(View, { style: styles.container },
|
|
81
|
+
React.createElement(TouchableOpacity, { style: styles.row, onPress: onCamera },
|
|
82
|
+
React.createElement(Image, { source: Images.video_icon, style: styles.icon }),
|
|
83
|
+
React.createElement(Text, { style: styles.text }, "Camera")),
|
|
84
|
+
React.createElement(Dividers, { small: true }),
|
|
85
|
+
React.createElement(TouchableOpacity, { style: styles.row, onPress: onGallery },
|
|
86
|
+
React.createElement(Image, { source: Images.image_icon, style: styles.icon }),
|
|
87
|
+
React.createElement(Text, { style: styles.text }, "Gallery")),
|
|
88
|
+
React.createElement(Dividers, { small: true }),
|
|
89
|
+
React.createElement(TouchableOpacity, { style: styles.row, onPress: onClose },
|
|
90
|
+
React.createElement(Image, { source: Images.image_icon, style: styles.icon }),
|
|
91
|
+
React.createElement(Text, { style: styles.text }, "Cancel")))));
|
|
92
|
+
};
|
|
93
|
+
export default ImagePicker;
|
|
94
|
+
const styles = StyleSheet.create({
|
|
95
|
+
container: { flex: 1, padding: 16 },
|
|
96
|
+
row: {
|
|
97
|
+
flexDirection: "row",
|
|
98
|
+
alignItems: "center",
|
|
99
|
+
// paddingVertical: 12,
|
|
100
|
+
},
|
|
101
|
+
text: { ...Typography.style.standardU(), color: Colors.black },
|
|
102
|
+
separator: { height: 1, backgroundColor: Colors.borderGrey, marginVertical: 8 },
|
|
103
|
+
icon: {
|
|
104
|
+
width: Scale.moderateScale(20),
|
|
105
|
+
height: Scale.moderateScale(20),
|
|
106
|
+
marginRight: 10,
|
|
107
|
+
tintColor: Colors.darkBlue,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { TextInputProps, ImageSourcePropType } from "react-native";
|
|
3
|
+
import { InputType } from "../../utils/regex";
|
|
4
|
+
interface InputProps extends TextInputProps {
|
|
5
|
+
intlType?: string;
|
|
6
|
+
textKey?: string;
|
|
7
|
+
leftIcon?: ImageSourcePropType;
|
|
8
|
+
rightIcon?: ImageSourcePropType;
|
|
9
|
+
onLeftIconPress?: () => void;
|
|
10
|
+
onRightIconPress?: () => void;
|
|
11
|
+
type?: InputType;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
/** error message from parent (Formik, RHF, Zod, etc.) */
|
|
14
|
+
error?: string | boolean;
|
|
15
|
+
value?: string;
|
|
16
|
+
customRegex?: RegExp;
|
|
17
|
+
customErrorMessage?: string;
|
|
18
|
+
onChangeText?: (text: string) => void;
|
|
19
|
+
onValidation?: (errorObj: {
|
|
20
|
+
type: InputType;
|
|
21
|
+
hasError: boolean;
|
|
22
|
+
message?: string;
|
|
23
|
+
}) => void;
|
|
24
|
+
}
|
|
25
|
+
declare const Input: React.FC<InputProps>;
|
|
26
|
+
export default Input;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React, { useState, useCallback, useEffect } from "react";
|
|
2
|
+
import { View, TextInput, StyleSheet, Image, TouchableOpacity, Text, } from "react-native";
|
|
3
|
+
import { useTranslation } from "react-i18next";
|
|
4
|
+
import { Colors, Typography, Scale, Images } from "../../styles";
|
|
5
|
+
import { defaultRegex } from "../../utils/regex";
|
|
6
|
+
import { messages } from "../../constants/messages";
|
|
7
|
+
const Input = ({ intlType, textKey, placeholder, leftIcon, rightIcon, onLeftIconPress, onRightIconPress, style, type = "text", disabled = false, error, value = "", customRegex, customErrorMessage, onChangeText, onValidation, onBlur: restOnBlur, onFocus: restOnFocus, ...rest }) => {
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
const [secureText, setSecureText] = useState(type === "password");
|
|
10
|
+
const [internalError, setInternalError] = useState(null);
|
|
11
|
+
const [wasBlurred, setWasBlurred] = useState(false);
|
|
12
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
13
|
+
const translatedPlaceholder = intlType && textKey ? String(t(textKey, { ns: intlType, value })) : placeholder;
|
|
14
|
+
const keyboardType = type === "email" ? "email-address" : type === "number" ? "numeric" : "default";
|
|
15
|
+
// internal validator (non-Formik, non-RHF)
|
|
16
|
+
const validateInternal = useCallback((text) => {
|
|
17
|
+
if (!wasBlurred)
|
|
18
|
+
return;
|
|
19
|
+
const regexToUse = customRegex ?? defaultRegex[type];
|
|
20
|
+
const fallbackMsg = messages.invalid[type] || "Invalid input";
|
|
21
|
+
const msg = customErrorMessage || fallbackMsg;
|
|
22
|
+
if (text === "" || !regexToUse.test(text)) {
|
|
23
|
+
setInternalError(msg);
|
|
24
|
+
onValidation?.({ type, hasError: true, message: msg });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
setInternalError(null);
|
|
28
|
+
onValidation?.({ type, hasError: false });
|
|
29
|
+
}
|
|
30
|
+
}, [wasBlurred, type, customRegex, customErrorMessage, onValidation]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!customRegex)
|
|
33
|
+
return;
|
|
34
|
+
if (internalError && value && customRegex.test(value)) {
|
|
35
|
+
setInternalError(null);
|
|
36
|
+
onValidation?.({ type, hasError: false });
|
|
37
|
+
}
|
|
38
|
+
}, [value]);
|
|
39
|
+
const handleChangeText = (text) => {
|
|
40
|
+
onChangeText?.(text);
|
|
41
|
+
if (wasBlurred)
|
|
42
|
+
validateInternal(text);
|
|
43
|
+
};
|
|
44
|
+
const handleFocus = (e) => {
|
|
45
|
+
setIsFocused(true);
|
|
46
|
+
restOnFocus?.(e);
|
|
47
|
+
};
|
|
48
|
+
const handleBlur = (e) => {
|
|
49
|
+
setIsFocused(false);
|
|
50
|
+
if (!wasBlurred)
|
|
51
|
+
setWasBlurred(true);
|
|
52
|
+
validateInternal(value || "");
|
|
53
|
+
restOnBlur?.(e);
|
|
54
|
+
};
|
|
55
|
+
// Decide which error to show: external error (Formik/RHF/etc) > internal error
|
|
56
|
+
const rawError = (typeof error === "string" ? error : undefined) ?? internalError ?? null;
|
|
57
|
+
const showError = (() => {
|
|
58
|
+
if (!rawError)
|
|
59
|
+
return null;
|
|
60
|
+
return wasBlurred && !isFocused ? rawError : null;
|
|
61
|
+
})();
|
|
62
|
+
return (React.createElement(View, { style: { marginBottom: 10 } },
|
|
63
|
+
React.createElement(View, { style: [
|
|
64
|
+
styles.container,
|
|
65
|
+
disabled && styles.disabled,
|
|
66
|
+
!!showError && styles.errorBorder,
|
|
67
|
+
style,
|
|
68
|
+
] },
|
|
69
|
+
leftIcon && (React.createElement(TouchableOpacity, { onPress: onLeftIconPress, disabled: !onLeftIconPress, style: styles.iconWrapper },
|
|
70
|
+
React.createElement(Image, { source: leftIcon, style: styles.icon, resizeMode: "contain" }))),
|
|
71
|
+
React.createElement(TextInput, { style: [styles.input, disabled && styles.disabledText], placeholder: translatedPlaceholder, placeholderTextColor: Colors.textGrey, editable: !disabled, secureTextEntry: secureText, keyboardType: keyboardType, value: value, onChangeText: handleChangeText, onFocus: handleFocus, onBlur: handleBlur, ...rest }),
|
|
72
|
+
type === "password" ? (React.createElement(TouchableOpacity, { onPress: () => setSecureText(!secureText), style: styles.iconWrapper },
|
|
73
|
+
React.createElement(Image, { source: secureText ? Images.Eyeoff : Images.Eyeon, style: styles.icon, resizeMode: "contain" }))) : (rightIcon && (React.createElement(TouchableOpacity, { onPress: onRightIconPress, disabled: !onRightIconPress, style: styles.iconWrapper },
|
|
74
|
+
React.createElement(Image, { source: rightIcon, style: styles.icon, resizeMode: "contain" }))))),
|
|
75
|
+
showError && React.createElement(Text, { style: styles.errorText }, showError)));
|
|
76
|
+
};
|
|
77
|
+
export default Input;
|
|
78
|
+
const styles = StyleSheet.create({
|
|
79
|
+
container: {
|
|
80
|
+
flexDirection: "row",
|
|
81
|
+
alignItems: "center",
|
|
82
|
+
borderRadius: 5,
|
|
83
|
+
borderWidth: 1,
|
|
84
|
+
borderColor: Colors.primaryColor,
|
|
85
|
+
backgroundColor: Colors.white,
|
|
86
|
+
height: Scale.moderateScale(50),
|
|
87
|
+
paddingHorizontal: Scale.moderateScale(10),
|
|
88
|
+
},
|
|
89
|
+
input: {
|
|
90
|
+
flex: 1,
|
|
91
|
+
...Typography.style.standardU(),
|
|
92
|
+
textTransform: "none",
|
|
93
|
+
color: Colors.textColor,
|
|
94
|
+
},
|
|
95
|
+
iconWrapper: {
|
|
96
|
+
padding: Scale.moderateScale(5),
|
|
97
|
+
},
|
|
98
|
+
icon: {
|
|
99
|
+
width: Scale.moderateScale(20),
|
|
100
|
+
height: Scale.moderateScale(20),
|
|
101
|
+
tintColor: Colors.primaryColor,
|
|
102
|
+
},
|
|
103
|
+
disabled: {
|
|
104
|
+
backgroundColor: Colors.lightGrey,
|
|
105
|
+
borderColor: Colors.borderGrey,
|
|
106
|
+
},
|
|
107
|
+
disabledText: {
|
|
108
|
+
color: Colors.disabledGrey,
|
|
109
|
+
},
|
|
110
|
+
errorBorder: {
|
|
111
|
+
borderColor: Colors.dangerRed,
|
|
112
|
+
},
|
|
113
|
+
errorText: {
|
|
114
|
+
color: Colors.dangerRed,
|
|
115
|
+
fontSize: Scale.moderateScale(12),
|
|
116
|
+
marginTop: 3,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { ModalProps } from 'react-native-modal';
|
|
3
|
+
type AppModalProps = Partial<ModalProps> & {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
closing: () => void;
|
|
6
|
+
bkO?: number;
|
|
7
|
+
};
|
|
8
|
+
declare const Modals: ({ children, closing, bkO, ...props }: AppModalProps) => React.JSX.Element;
|
|
9
|
+
export default Modals;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Modal from 'react-native-modal';
|
|
3
|
+
// Base Modal Component customized for app needs
|
|
4
|
+
const Modals = ({ children, closing, bkO = 0.5, ...props }) => (React.createElement(Modal, { backdropOpacity: bkO, animationInTiming: 400, animationOutTiming: 400, backdropTransitionOutTiming: 0, swipeDirection: ['down', 'left', 'right', 'up'], onSwipeComplete: closing, onBackdropPress: closing, onBackButtonPress: closing, ...props }, children));
|
|
5
|
+
export default Modals;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ColorValue } from 'react-native';
|
|
3
|
+
interface MyStatusBarProps {
|
|
4
|
+
backgroundColor?: ColorValue;
|
|
5
|
+
barStyle?: 'default' | 'light-content' | 'dark-content';
|
|
6
|
+
}
|
|
7
|
+
declare const MyStatusBar: React.FC<MyStatusBarProps>;
|
|
8
|
+
export default MyStatusBar;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StatusBar, SafeAreaView, Platform, StyleSheet, } from 'react-native';
|
|
3
|
+
const MyStatusBar = ({ backgroundColor = '#fff', barStyle = 'default', }) => {
|
|
4
|
+
return (React.createElement(SafeAreaView, { style: [styles.safeArea, { backgroundColor }] },
|
|
5
|
+
React.createElement(StatusBar, { barStyle: barStyle, backgroundColor: Platform.OS === 'android' ? backgroundColor : undefined, translucent: false })));
|
|
6
|
+
};
|
|
7
|
+
const styles = StyleSheet.create({
|
|
8
|
+
safeArea: {
|
|
9
|
+
backgroundColor: '#fff',
|
|
10
|
+
// height: Platform.OS === 'android' ? StatusBar.currentHeight : undefined,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
export default MyStatusBar;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet, Dimensions } from 'react-native';
|
|
3
|
+
import { Colors, Scale } from '../../styles';
|
|
4
|
+
const { width } = Dimensions.get('window');
|
|
5
|
+
const ProgressBar = ({ progress, activeColor = Colors.primaryColor, inActiveColor = Colors.disabledGrey, containerStyle }) => {
|
|
6
|
+
return (React.createElement(View, { style: [
|
|
7
|
+
styles.container, containerStyle,
|
|
8
|
+
{ backgroundColor: inActiveColor },
|
|
9
|
+
] },
|
|
10
|
+
React.createElement(View, { style: [
|
|
11
|
+
styles.progress,
|
|
12
|
+
{ width: `${progress * 100}%`, backgroundColor: activeColor },
|
|
13
|
+
] })));
|
|
14
|
+
};
|
|
15
|
+
const styles = StyleSheet.create({
|
|
16
|
+
container: {
|
|
17
|
+
height: Scale.moderateScale(18),
|
|
18
|
+
width: width,
|
|
19
|
+
borderRadius: 10,
|
|
20
|
+
overflow: 'hidden',
|
|
21
|
+
},
|
|
22
|
+
progress: {
|
|
23
|
+
height: '100%',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle, TextStyle } from 'react-native';
|
|
3
|
+
interface AppRadioButtonProps {
|
|
4
|
+
label?: string;
|
|
5
|
+
intlType?: string;
|
|
6
|
+
selected: boolean;
|
|
7
|
+
onSelect: () => void;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
size?: number;
|
|
10
|
+
color?: string;
|
|
11
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
12
|
+
outerStyle?: StyleProp<ViewStyle>;
|
|
13
|
+
innerStyle?: StyleProp<ViewStyle>;
|
|
14
|
+
labelStyle?: StyleProp<TextStyle>;
|
|
15
|
+
renderLabel?: () => React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
declare const RadioButton: React.FC<AppRadioButtonProps>;
|
|
18
|
+
export default RadioButton;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet, } from 'react-native';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { Colors } from '../../styles';
|
|
5
|
+
const RadioButton = ({ label, intlType, selected, onSelect, disabled = false, size = 20, color = Colors.checkedColor, containerStyle, outerStyle, innerStyle, labelStyle, renderLabel, }) => {
|
|
6
|
+
const { t } = useTranslation();
|
|
7
|
+
const getLabel = () => {
|
|
8
|
+
if (!label)
|
|
9
|
+
return '';
|
|
10
|
+
if (intlType) {
|
|
11
|
+
const translated = t(label, { ns: intlType });
|
|
12
|
+
if (translated !== label)
|
|
13
|
+
return translated;
|
|
14
|
+
}
|
|
15
|
+
return label;
|
|
16
|
+
};
|
|
17
|
+
return (React.createElement(TouchableOpacity, { style: [styles.container, containerStyle], onPress: onSelect, disabled: disabled },
|
|
18
|
+
React.createElement(View, { style: [
|
|
19
|
+
{
|
|
20
|
+
width: size,
|
|
21
|
+
height: size,
|
|
22
|
+
borderRadius: size / 2,
|
|
23
|
+
borderWidth: 1,
|
|
24
|
+
borderColor: color,
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
justifyContent: 'center',
|
|
27
|
+
marginRight: 10,
|
|
28
|
+
},
|
|
29
|
+
outerStyle,
|
|
30
|
+
] }, selected && (React.createElement(View, { style: [
|
|
31
|
+
{
|
|
32
|
+
width: size / 2,
|
|
33
|
+
height: size / 2,
|
|
34
|
+
borderRadius: size / 4,
|
|
35
|
+
backgroundColor: color,
|
|
36
|
+
},
|
|
37
|
+
innerStyle,
|
|
38
|
+
] }))),
|
|
39
|
+
renderLabel
|
|
40
|
+
? renderLabel()
|
|
41
|
+
: label
|
|
42
|
+
? React.createElement(Text, { style: [styles.label, labelStyle] }, getLabel())
|
|
43
|
+
: null));
|
|
44
|
+
};
|
|
45
|
+
const styles = StyleSheet.create({
|
|
46
|
+
container: { flexDirection: 'row', alignItems: 'center' },
|
|
47
|
+
label: { fontSize: 14, color: '#000' },
|
|
48
|
+
});
|
|
49
|
+
export default RadioButton;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { ViewStyle } from 'react-native';
|
|
3
|
+
type ScrolledContainerProps = {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
containerStyle?: ViewStyle;
|
|
6
|
+
safeEdgesTop?: boolean;
|
|
7
|
+
safeEdgesBottom?: boolean;
|
|
8
|
+
addPaddingTop?: boolean;
|
|
9
|
+
scrollEnabled?: boolean;
|
|
10
|
+
keyboardShouldPersistTaps?: 'always' | 'handled' | 'never';
|
|
11
|
+
};
|
|
12
|
+
declare const ScrolledContainer: ({ containerStyle, children, safeEdgesTop, safeEdgesBottom, addPaddingTop, scrollEnabled, keyboardShouldPersistTaps, }: ScrolledContainerProps) => React.JSX.Element;
|
|
13
|
+
export default ScrolledContainer;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { StyleSheet, Platform } from 'react-native';
|
|
3
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
4
|
+
import { Colors, Scale } from '../../styles';
|
|
5
|
+
import { ScrollView } from 'react-native';
|
|
6
|
+
const ScrolledContainer = ({ containerStyle, children, safeEdgesTop, safeEdgesBottom, addPaddingTop, scrollEnabled = true, keyboardShouldPersistTaps = 'handled', }) => {
|
|
7
|
+
const edges = ['left', 'right'];
|
|
8
|
+
if (safeEdgesTop)
|
|
9
|
+
edges.push('top');
|
|
10
|
+
if (safeEdgesBottom)
|
|
11
|
+
edges.push('bottom');
|
|
12
|
+
return (React.createElement(SafeAreaView, { edges: edges, style: styles.globalSafeAreaStyle },
|
|
13
|
+
React.createElement(ScrollView, { contentContainerStyle: [
|
|
14
|
+
styles.screen,
|
|
15
|
+
addPaddingTop && { paddingTop: Scale?.moderateScale(50) },
|
|
16
|
+
containerStyle,
|
|
17
|
+
], showsVerticalScrollIndicator: false, scrollEnabled: scrollEnabled, keyboardShouldPersistTaps: keyboardShouldPersistTaps }, children)));
|
|
18
|
+
};
|
|
19
|
+
export default ScrolledContainer;
|
|
20
|
+
const styles = StyleSheet.create({
|
|
21
|
+
globalSafeAreaStyle: {
|
|
22
|
+
flex: 1,
|
|
23
|
+
backgroundColor: Colors.background,
|
|
24
|
+
paddingBottom: Platform.OS === 'android' ? Scale?.moderateScale(10) : 0,
|
|
25
|
+
},
|
|
26
|
+
screen: {
|
|
27
|
+
flexGrow: 1, // makes ScrollView expand & scroll
|
|
28
|
+
paddingTop: Scale?.moderateScale(20),
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ViewStyle, ActivityIndicatorProps } from 'react-native';
|
|
3
|
+
interface AppSpinnerProps extends ActivityIndicatorProps {
|
|
4
|
+
loaderStyle?: ViewStyle;
|
|
5
|
+
size?: 'small' | 'large';
|
|
6
|
+
}
|
|
7
|
+
declare const Spinner: React.FC<AppSpinnerProps>;
|
|
8
|
+
export default Spinner;
|