@teamnhz/rn-ui-toolkit 1.1.6 → 1.1.8
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/components/AppHeader/index.d.ts +1 -16
- package/dist/components/AppHeader/index.js +39 -18
- package/dist/components/ImagePicker/index.d.ts +2 -1
- package/dist/components/ImagePicker/index.js +262 -147
- package/dist/components/Input/index.d.ts +1 -0
- package/dist/components/Input/index.js +2 -238
- package/package.json +4 -2
|
@@ -1,17 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
navigation: any;
|
|
3
|
-
headerTitle?: string;
|
|
4
|
-
titleStyle?: object;
|
|
5
|
-
leftImage?: any;
|
|
6
|
-
onPressLeft?: () => void;
|
|
7
|
-
leftIconStyle?: object;
|
|
8
|
-
rightImageOne?: any;
|
|
9
|
-
onPressRightOne?: () => void;
|
|
10
|
-
rightImageOneStyle?: object;
|
|
11
|
-
rightImageTwo?: any;
|
|
12
|
-
onPressRightTwo?: () => void;
|
|
13
|
-
rightImageTwoStyle?: object;
|
|
14
|
-
headerStyle?: object;
|
|
15
|
-
};
|
|
16
|
-
declare const AppHeader: (props: AppHeaderProps) => void;
|
|
1
|
+
declare const AppHeader: (props: any) => any;
|
|
17
2
|
export default AppHeader;
|
|
@@ -1,45 +1,66 @@
|
|
|
1
|
-
import { View, TouchableOpacity, StyleSheet } from "react-native";
|
|
2
1
|
import React from "react";
|
|
2
|
+
import { View, TouchableOpacity, StyleSheet, Platform, StatusBar, } from "react-native";
|
|
3
3
|
import { Colors } from "../../styles";
|
|
4
4
|
import { Image, T } from "../index";
|
|
5
|
+
const getSafeAreaTop = () => {
|
|
6
|
+
if (Platform.OS === "android") {
|
|
7
|
+
return StatusBar.currentHeight || 0;
|
|
8
|
+
}
|
|
9
|
+
return 20; // iOS safe area fallback
|
|
10
|
+
};
|
|
5
11
|
const AppHeader = (props) => {
|
|
6
12
|
const { navigation, headerTitle, titleStyle, leftImage, onPressLeft, leftIconStyle, rightImageOne, onPressRightOne, rightImageOneStyle, rightImageTwo, onPressRightTwo, rightImageTwoStyle, headerStyle, } = props;
|
|
13
|
+
const safeTop = getSafeAreaTop();
|
|
7
14
|
navigation.setOptions({
|
|
15
|
+
headerLeftContainerStyle: {
|
|
16
|
+
paddingLeft: 12,
|
|
17
|
+
paddingBottom: Platform.OS === "ios" ? 4 : 0,
|
|
18
|
+
},
|
|
19
|
+
headerRightContainerStyle: {
|
|
20
|
+
paddingRight: 12,
|
|
21
|
+
paddingBottom: Platform.OS === "ios" ? 4 : 0,
|
|
22
|
+
},
|
|
23
|
+
headerTitleContainerStyle: {
|
|
24
|
+
paddingBottom: Platform.OS === "ios" ? 4 : 0,
|
|
25
|
+
},
|
|
26
|
+
headerStyle: [
|
|
27
|
+
styles.headerBase,
|
|
28
|
+
{ paddingTop: safeTop },
|
|
29
|
+
headerStyle,
|
|
30
|
+
],
|
|
8
31
|
headerLeft: () => leftImage ? (React.createElement(TouchableOpacity, { onPress: onPressLeft, activeOpacity: 0.8 },
|
|
9
|
-
React.createElement(Image, { source: leftImage, resizeMode: "contain", style: [styles.
|
|
32
|
+
React.createElement(Image, { source: leftImage, resizeMode: "contain", style: [styles.icon, leftIconStyle] }))) : null,
|
|
10
33
|
headerTitle: () => headerTitle ? (React.createElement(T, { title: headerTitle, style: [styles.headerTitleText, titleStyle] })) : null,
|
|
11
34
|
headerRight: () => (React.createElement(View, { style: styles.headerRightWrapper },
|
|
12
35
|
rightImageOne ? (React.createElement(TouchableOpacity, { onPress: onPressRightOne, activeOpacity: 0.8 },
|
|
13
|
-
React.createElement(Image, { source: rightImageOne, resizeMode: "contain", style: [
|
|
14
|
-
styles.rightImage,
|
|
15
|
-
rightImageOneStyle,
|
|
16
|
-
{ marginRight: 10 },
|
|
17
|
-
] }))) : null,
|
|
36
|
+
React.createElement(Image, { source: rightImageOne, resizeMode: "contain", style: [styles.icon, rightImageOneStyle] }))) : null,
|
|
18
37
|
rightImageTwo ? (React.createElement(TouchableOpacity, { onPress: onPressRightTwo, activeOpacity: 0.8 },
|
|
19
|
-
React.createElement(Image, { source: rightImageTwo, resizeMode: "contain", style: [styles.
|
|
20
|
-
headerStyle: [styles.headerBase, headerStyle],
|
|
38
|
+
React.createElement(Image, { source: rightImageTwo, resizeMode: "contain", style: [styles.icon, rightImageTwoStyle] }))) : null)),
|
|
21
39
|
});
|
|
40
|
+
return null;
|
|
22
41
|
};
|
|
23
42
|
export default AppHeader;
|
|
24
43
|
const styles = StyleSheet.create({
|
|
25
44
|
headerBase: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
width: 44,
|
|
45
|
+
backgroundColor: Colors.white,
|
|
46
|
+
borderBottomWidth: 1,
|
|
47
|
+
borderBottomColor: Colors.lightGrey,
|
|
48
|
+
height: Platform.OS === "ios" ? 90 : 70,
|
|
49
|
+
justifyContent: "center",
|
|
32
50
|
},
|
|
33
|
-
|
|
34
|
-
height:
|
|
35
|
-
width:
|
|
51
|
+
icon: {
|
|
52
|
+
height: 28,
|
|
53
|
+
width: 28,
|
|
36
54
|
},
|
|
37
55
|
headerRightWrapper: {
|
|
38
56
|
flexDirection: "row",
|
|
39
57
|
alignItems: "center",
|
|
58
|
+
gap: 12,
|
|
40
59
|
},
|
|
41
60
|
headerTitleText: {
|
|
42
61
|
color: Colors.black,
|
|
43
62
|
textAlign: "center",
|
|
63
|
+
fontSize: 18,
|
|
64
|
+
fontWeight: "600",
|
|
44
65
|
},
|
|
45
66
|
});
|
|
@@ -2,9 +2,10 @@ import React from "react";
|
|
|
2
2
|
type Props = {
|
|
3
3
|
mediaType: "photo" | "video";
|
|
4
4
|
isMultiSelect?: boolean;
|
|
5
|
-
onSuccess: (
|
|
5
|
+
onSuccess: (response: any) => void;
|
|
6
6
|
visible: boolean;
|
|
7
7
|
onClose: () => void;
|
|
8
|
+
enableCompression?: boolean;
|
|
8
9
|
};
|
|
9
10
|
declare const ImagePicker: React.FC<Props>;
|
|
10
11
|
export default ImagePicker;
|
|
@@ -6,38 +6,49 @@
|
|
|
6
6
|
// StyleSheet,
|
|
7
7
|
// Image,
|
|
8
8
|
// Alert,
|
|
9
|
-
// ViewStyle,
|
|
10
|
-
// TextStyle,
|
|
11
|
-
// ImageStyle,
|
|
12
9
|
// } from "react-native";
|
|
13
10
|
// import { BottomSheet, Dividers } from "../index";
|
|
14
11
|
// import { launchCamera, launchImageLibrary } from "react-native-image-picker";
|
|
15
12
|
// import ImageCropPicker from "react-native-image-crop-picker";
|
|
13
|
+
// import {
|
|
14
|
+
// Image as ImageCompressor,
|
|
15
|
+
// Video as VideoCompressor,
|
|
16
|
+
// } from "react-native-compressor";
|
|
17
|
+
// import RNFS from "react-native-fs";
|
|
16
18
|
// import { Colors, Images, Scale, Typography } from "../../styles";
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// };
|
|
19
|
+
// // Import your permission utilities
|
|
20
|
+
// import {
|
|
21
|
+
// cameraPermissions,
|
|
22
|
+
// galleryPermissions,
|
|
23
|
+
// checkMicroPhonePermission,
|
|
24
|
+
// } from "../../utils/permissions";
|
|
23
25
|
// type Props = {
|
|
24
26
|
// mediaType: "photo" | "video";
|
|
25
27
|
// isMultiSelect?: boolean;
|
|
26
28
|
// onSuccess: (data: any) => void;
|
|
27
29
|
// visible: boolean;
|
|
28
30
|
// onClose: () => void;
|
|
29
|
-
//
|
|
31
|
+
// // Compression options
|
|
32
|
+
// enableCompression?: boolean;
|
|
33
|
+
// imageCompressionOptions?: {
|
|
34
|
+
// maxWidth?: number;
|
|
35
|
+
// quality?: number;
|
|
36
|
+
// };
|
|
37
|
+
// videoCompressionOptions?: {
|
|
38
|
+
// compressionMethod?: "auto" | "manual";
|
|
39
|
+
// };
|
|
40
|
+
// };
|
|
30
41
|
// const ImagePicker: React.FC<Props> = ({
|
|
31
42
|
// mediaType,
|
|
32
43
|
// isMultiSelect = false,
|
|
33
44
|
// onSuccess,
|
|
34
45
|
// visible,
|
|
35
46
|
// onClose,
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// iconStyle,
|
|
47
|
+
// enableCompression = true,
|
|
48
|
+
// imageCompressionOptions = { maxWidth: 1080, quality: 0.7 },
|
|
49
|
+
// videoCompressionOptions = { compressionMethod: "auto" },
|
|
40
50
|
// }) => {
|
|
51
|
+
// // Success handler
|
|
41
52
|
// const onComplete = useCallback(
|
|
42
53
|
// (data: any) => {
|
|
43
54
|
// onSuccess(data);
|
|
@@ -45,98 +56,184 @@
|
|
|
45
56
|
// },
|
|
46
57
|
// [onSuccess, onClose]
|
|
47
58
|
// );
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
59
|
+
// // Helper: Compress Image
|
|
60
|
+
// const compressImage = async (imagePath: string) => {
|
|
61
|
+
// if (!enableCompression) return imagePath;
|
|
62
|
+
// try {
|
|
63
|
+
// const compressedImage = await ImageCompressor.compress(imagePath, {
|
|
64
|
+
// maxWidth: imageCompressionOptions.maxWidth,
|
|
65
|
+
// quality: imageCompressionOptions.quality,
|
|
66
|
+
// });
|
|
67
|
+
// // Log compressed size (optional)
|
|
68
|
+
// const stats = await RNFS.stat(compressedImage.replace("file://", ""));
|
|
69
|
+
// const sizeInMB = stats.size / (1024 * 1024);
|
|
70
|
+
// console.log("Compressed Image Size (MB):", sizeInMB);
|
|
71
|
+
// return compressedImage;
|
|
72
|
+
// } catch (error) {
|
|
73
|
+
// console.error("Image compression error:", error);
|
|
74
|
+
// return imagePath; // Return original if compression fails
|
|
75
|
+
// }
|
|
76
|
+
// };
|
|
77
|
+
// // Helper: Compress Video
|
|
78
|
+
// const compressVideo = async (videoUri: string) => {
|
|
79
|
+
// if (!enableCompression) return videoUri;
|
|
80
|
+
// try {
|
|
81
|
+
// const compressedVideo = await VideoCompressor.compress(videoUri, {
|
|
82
|
+
// compressionMethod: videoCompressionOptions.compressionMethod,
|
|
83
|
+
// });
|
|
84
|
+
// // Log compressed size (optional)
|
|
85
|
+
// const stats = await RNFS.stat(compressedVideo.replace("file://", ""));
|
|
86
|
+
// const sizeInMB = stats.size / (1024 * 1024);
|
|
87
|
+
// console.log("Compressed Video Size (MB):", sizeInMB);
|
|
88
|
+
// return compressedVideo;
|
|
89
|
+
// } catch (error) {
|
|
90
|
+
// console.error("Video compression error:", error);
|
|
91
|
+
// return videoUri; // Return original if compression fails
|
|
92
|
+
// }
|
|
93
|
+
// };
|
|
94
|
+
// // CAMERA HANDLER with permission check and compression
|
|
95
|
+
// const handleCamera = async () => {
|
|
96
|
+
// await cameraPermissions(async (granted: boolean) => {
|
|
97
|
+
// if (!granted) return;
|
|
98
|
+
// if (mediaType === "photo") {
|
|
99
|
+
// try {
|
|
100
|
+
// const response = await ImageCropPicker.openCamera({
|
|
101
|
+
// mediaType: "photo",
|
|
102
|
+
// });
|
|
103
|
+
// if (isMultiSelect) {
|
|
104
|
+
// // Single capture for multi-select mode
|
|
105
|
+
// const compressedPath = await compressImage(response.path);
|
|
106
|
+
// onComplete([{ ...response, path: compressedPath }]);
|
|
107
|
+
// } else {
|
|
108
|
+
// // Crop and compress single image
|
|
109
|
+
// const cropped = await ImageCropPicker.openCropper({
|
|
58
110
|
// path: response?.path,
|
|
59
111
|
// width: response?.width,
|
|
60
112
|
// height: response?.height,
|
|
61
113
|
// mediaType: "photo",
|
|
62
114
|
// freeStyleCropEnabled: true,
|
|
63
|
-
// })
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
// if (response?.assets) {
|
|
70
|
-
// onComplete({
|
|
71
|
-
// path: response.assets[0]?.uri,
|
|
72
|
-
// duration: response.assets[0]?.duration,
|
|
73
|
-
// });
|
|
115
|
+
// });
|
|
116
|
+
// const compressedPath = await compressImage(cropped.path);
|
|
117
|
+
// onComplete({ ...cropped, path: compressedPath });
|
|
118
|
+
// }
|
|
119
|
+
// } catch (error) {
|
|
120
|
+
// console.log("Camera cancelled or error:", error);
|
|
74
121
|
// }
|
|
75
|
-
// }
|
|
76
|
-
//
|
|
122
|
+
// } else {
|
|
123
|
+
// // VIDEO CAPTURE - check microphone permission first
|
|
124
|
+
// const micPermission = await checkMicroPhonePermission();
|
|
125
|
+
// if (!micPermission) {
|
|
126
|
+
// Alert.alert(
|
|
127
|
+
// "Microphone Permission Required",
|
|
128
|
+
// "Please enable microphone access to record videos."
|
|
129
|
+
// );
|
|
130
|
+
// return;
|
|
131
|
+
// }
|
|
132
|
+
// launchCamera(
|
|
133
|
+
// {
|
|
134
|
+
// mediaType: "video",
|
|
135
|
+
// durationLimit: 60,
|
|
136
|
+
// videoQuality: "high",
|
|
137
|
+
// },
|
|
138
|
+
// async (response) => {
|
|
139
|
+
// if (response?.assets?.length) {
|
|
140
|
+
// const videoUri = response.assets[0]?.uri;
|
|
141
|
+
// const compressedPath = await compressVideo(videoUri);
|
|
142
|
+
// onComplete({
|
|
143
|
+
// path: compressedPath,
|
|
144
|
+
// duration: response.assets[0]?.duration,
|
|
145
|
+
// });
|
|
146
|
+
// }
|
|
147
|
+
// }
|
|
148
|
+
// );
|
|
149
|
+
// }
|
|
150
|
+
// });
|
|
77
151
|
// };
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
152
|
+
// // GALLERY HANDLER with permission check and compression
|
|
153
|
+
// const handleGallery = async () => {
|
|
154
|
+
// await galleryPermissions(async (granted: boolean) => {
|
|
155
|
+
// if (!granted) return;
|
|
156
|
+
// if (mediaType === "photo") {
|
|
157
|
+
// try {
|
|
158
|
+
// if (isMultiSelect) {
|
|
159
|
+
// const images = await ImageCropPicker.openPicker({
|
|
160
|
+
// mediaType: "photo",
|
|
161
|
+
// multiple: true,
|
|
162
|
+
// maxFiles: 5,
|
|
163
|
+
// });
|
|
87
164
|
// if (images.length > 5) {
|
|
88
165
|
// Alert.alert(
|
|
89
166
|
// "Limit Exceeded",
|
|
90
167
|
// "You can only select up to 5 images."
|
|
91
168
|
// );
|
|
92
|
-
//
|
|
93
|
-
// onComplete(images);
|
|
169
|
+
// return;
|
|
94
170
|
// }
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
171
|
+
// // Compress multiple images
|
|
172
|
+
// const compressedImages = await Promise.all(
|
|
173
|
+
// images.map(async (img) => ({
|
|
174
|
+
// ...img,
|
|
175
|
+
// path: await compressImage(img.path),
|
|
176
|
+
// }))
|
|
177
|
+
// );
|
|
178
|
+
// onComplete(compressedImages);
|
|
179
|
+
// } else {
|
|
180
|
+
// // Single image with crop
|
|
181
|
+
// const response = await ImageCropPicker.openPicker({
|
|
182
|
+
// mediaType: "photo",
|
|
183
|
+
// });
|
|
184
|
+
// const cropped = await ImageCropPicker.openCropper({
|
|
101
185
|
// path: response?.path,
|
|
102
186
|
// width: response?.width,
|
|
103
187
|
// height: response?.height,
|
|
104
188
|
// mediaType: "photo",
|
|
105
189
|
// freeStyleCropEnabled: true,
|
|
106
|
-
// })
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
112
|
-
// if (result?.assets) {
|
|
113
|
-
// onComplete({
|
|
114
|
-
// path: result.assets[0]?.uri,
|
|
115
|
-
// duration: result.assets[0]?.duration,
|
|
116
|
-
// });
|
|
190
|
+
// });
|
|
191
|
+
// const compressedPath = await compressImage(cropped.path);
|
|
192
|
+
// onComplete({ ...cropped, path: compressedPath });
|
|
193
|
+
// }
|
|
194
|
+
// } catch (error) {
|
|
195
|
+
// console.log("Gallery cancelled or error:", error);
|
|
117
196
|
// }
|
|
118
|
-
// }
|
|
119
|
-
//
|
|
197
|
+
// } else {
|
|
198
|
+
// // VIDEO FROM GALLERY
|
|
199
|
+
// launchImageLibrary(
|
|
200
|
+
// {
|
|
201
|
+
// mediaType: "video",
|
|
202
|
+
// assetRepresentationMode: "current",
|
|
203
|
+
// },
|
|
204
|
+
// async (result) => {
|
|
205
|
+
// if (result?.assets?.length) {
|
|
206
|
+
// const videoUri = result.assets[0]?.uri;
|
|
207
|
+
// const compressedPath = await compressVideo(videoUri);
|
|
208
|
+
// onComplete({
|
|
209
|
+
// path: compressedPath,
|
|
210
|
+
// duration: result.assets[0]?.duration,
|
|
211
|
+
// });
|
|
212
|
+
// }
|
|
213
|
+
// }
|
|
214
|
+
// );
|
|
215
|
+
// }
|
|
216
|
+
// });
|
|
120
217
|
// };
|
|
121
218
|
// return (
|
|
122
219
|
// <BottomSheet visible={visible} onClose={onClose} height={230}>
|
|
123
|
-
// <View style={
|
|
220
|
+
// <View style={styles.container}>
|
|
124
221
|
// {/* Camera */}
|
|
125
|
-
// <TouchableOpacity style={
|
|
126
|
-
// <Image source={Images.video_icon} style={
|
|
127
|
-
// <Text style={
|
|
222
|
+
// <TouchableOpacity style={styles.row} onPress={handleCamera}>
|
|
223
|
+
// <Image source={Images.video_icon} style={styles.icon} />
|
|
224
|
+
// <Text style={styles.text}>Camera</Text>
|
|
128
225
|
// </TouchableOpacity>
|
|
129
226
|
// <Dividers small />
|
|
130
227
|
// {/* Gallery */}
|
|
131
|
-
// <TouchableOpacity style={
|
|
132
|
-
// <Image source={Images.image_icon} style={
|
|
133
|
-
// <Text style={
|
|
228
|
+
// <TouchableOpacity style={styles.row} onPress={handleGallery}>
|
|
229
|
+
// <Image source={Images.image_icon} style={styles.icon} />
|
|
230
|
+
// <Text style={styles.text}>Gallery</Text>
|
|
134
231
|
// </TouchableOpacity>
|
|
135
232
|
// <Dividers small />
|
|
136
233
|
// {/* Cancel */}
|
|
137
|
-
// <TouchableOpacity style={
|
|
138
|
-
// <Image source={Images.cancel} style={
|
|
139
|
-
// <Text style={
|
|
234
|
+
// <TouchableOpacity style={styles.row} onPress={onClose}>
|
|
235
|
+
// <Image source={Images.cancel} style={styles.icon} />
|
|
236
|
+
// <Text style={styles.text}>Cancel</Text>
|
|
140
237
|
// </TouchableOpacity>
|
|
141
238
|
// </View>
|
|
142
239
|
// </BottomSheet>
|
|
@@ -157,100 +254,121 @@
|
|
|
157
254
|
// tintColor: Colors.white,
|
|
158
255
|
// },
|
|
159
256
|
// });
|
|
160
|
-
import React
|
|
161
|
-
import { View, Text, TouchableOpacity, StyleSheet, Image,
|
|
257
|
+
import React from "react";
|
|
258
|
+
import { View, Text, TouchableOpacity, StyleSheet, Image, } from "react-native";
|
|
162
259
|
import { BottomSheet, Dividers } from "../index";
|
|
163
260
|
import { launchCamera, launchImageLibrary } from "react-native-image-picker";
|
|
164
261
|
import ImageCropPicker from "react-native-image-crop-picker";
|
|
262
|
+
import { Image as ImageCompressor, Video as VideoCompressor, } from "react-native-compressor";
|
|
165
263
|
import { Colors, Images, Scale, Typography } from "../../styles";
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const onComplete = useCallback((data) => {
|
|
171
|
-
onSuccess(data);
|
|
264
|
+
import { cameraPermissions, galleryPermissions, checkMicroPhonePermission, } from "../../utils/permissions";
|
|
265
|
+
const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, enableCompression = true, }) => {
|
|
266
|
+
// Close modal + start loader in parent
|
|
267
|
+
const sendRawAndClose = (rawData) => {
|
|
172
268
|
onClose();
|
|
173
|
-
|
|
174
|
-
|
|
269
|
+
onSuccess({ loading: true, data: rawData });
|
|
270
|
+
};
|
|
271
|
+
// After compression finished
|
|
272
|
+
const sendFinalCompressed = (compressed) => {
|
|
273
|
+
onSuccess({ loading: false, data: compressed });
|
|
274
|
+
};
|
|
275
|
+
const compressImage = async (path) => {
|
|
276
|
+
try {
|
|
277
|
+
const compressed = await ImageCompressor.compress(path, {
|
|
278
|
+
maxWidth: 1080,
|
|
279
|
+
quality: 0.7,
|
|
280
|
+
});
|
|
281
|
+
return compressed;
|
|
282
|
+
}
|
|
283
|
+
catch (e) {
|
|
284
|
+
return path;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
const compressVideo = async (uri) => {
|
|
288
|
+
try {
|
|
289
|
+
const compressed = await VideoCompressor.compress(uri, {
|
|
290
|
+
compressionMethod: "auto",
|
|
291
|
+
});
|
|
292
|
+
return compressed;
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
return uri;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
// ---------- CAMERA ----------
|
|
175
299
|
const handleCamera = async () => {
|
|
176
300
|
await cameraPermissions(async (granted) => {
|
|
177
301
|
if (!granted)
|
|
178
|
-
return;
|
|
302
|
+
return;
|
|
179
303
|
if (mediaType === "photo") {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
183
|
-
.then((response) => onComplete([response]))
|
|
184
|
-
.catch(() => { });
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
ImageCropPicker.openCamera({ mediaType: "photo" })
|
|
188
|
-
.then((response) => ImageCropPicker.openCropper({
|
|
189
|
-
path: response?.path,
|
|
190
|
-
width: response?.width,
|
|
191
|
-
height: response?.height,
|
|
304
|
+
try {
|
|
305
|
+
const picked = await ImageCropPicker.openCamera({
|
|
192
306
|
mediaType: "photo",
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
307
|
+
});
|
|
308
|
+
sendRawAndClose(picked); // CLOSE MODAL + START LOADER
|
|
309
|
+
setTimeout(async () => {
|
|
310
|
+
const compressedPath = await compressImage(picked.path);
|
|
311
|
+
sendFinalCompressed({ ...picked, path: compressedPath });
|
|
312
|
+
}, 10);
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
console.log(err);
|
|
196
316
|
}
|
|
197
317
|
}
|
|
198
318
|
else {
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
319
|
+
// VIDEO
|
|
320
|
+
const mic = await checkMicroPhonePermission();
|
|
321
|
+
if (!mic)
|
|
322
|
+
return;
|
|
323
|
+
launchCamera({ mediaType: "video", durationLimit: 60, videoQuality: "high" }, (res) => {
|
|
324
|
+
if (!res?.assets?.length)
|
|
325
|
+
return;
|
|
326
|
+
const raw = res.assets[0];
|
|
327
|
+
sendRawAndClose(raw);
|
|
328
|
+
setTimeout(async () => {
|
|
329
|
+
const compressed = await compressVideo(raw.uri);
|
|
330
|
+
sendFinalCompressed({
|
|
331
|
+
path: compressed,
|
|
332
|
+
duration: raw.duration,
|
|
205
333
|
});
|
|
206
|
-
}
|
|
334
|
+
}, 10);
|
|
207
335
|
});
|
|
208
336
|
}
|
|
209
337
|
});
|
|
210
338
|
};
|
|
211
|
-
//
|
|
339
|
+
// ---------- GALLERY ----------
|
|
212
340
|
const handleGallery = async () => {
|
|
213
341
|
await galleryPermissions(async (granted) => {
|
|
214
342
|
if (!granted)
|
|
215
|
-
return;
|
|
343
|
+
return;
|
|
216
344
|
if (mediaType === "photo") {
|
|
217
|
-
|
|
218
|
-
ImageCropPicker.openPicker({
|
|
345
|
+
try {
|
|
346
|
+
const picked = await ImageCropPicker.openPicker({
|
|
219
347
|
mediaType: "photo",
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
onComplete(images);
|
|
229
|
-
}
|
|
230
|
-
})
|
|
231
|
-
.catch(() => { });
|
|
348
|
+
});
|
|
349
|
+
sendRawAndClose(picked);
|
|
350
|
+
setTimeout(async () => {
|
|
351
|
+
const compressed = await compressImage(picked.path);
|
|
352
|
+
sendFinalCompressed({ ...picked, path: compressed });
|
|
353
|
+
}, 10);
|
|
232
354
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
.then((response) => ImageCropPicker.openCropper({
|
|
236
|
-
path: response?.path,
|
|
237
|
-
width: response?.width,
|
|
238
|
-
height: response?.height,
|
|
239
|
-
mediaType: "photo",
|
|
240
|
-
freeStyleCropEnabled: true,
|
|
241
|
-
}).then(onComplete))
|
|
242
|
-
.catch(() => { });
|
|
355
|
+
catch (e) {
|
|
356
|
+
console.log(e);
|
|
243
357
|
}
|
|
244
358
|
}
|
|
245
359
|
else {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
360
|
+
launchImageLibrary({ mediaType: "video" }, (res) => {
|
|
361
|
+
if (!res?.assets?.length)
|
|
362
|
+
return;
|
|
363
|
+
const raw = res.assets[0];
|
|
364
|
+
sendRawAndClose(raw);
|
|
365
|
+
setTimeout(async () => {
|
|
366
|
+
const compressed = await compressVideo(raw.uri);
|
|
367
|
+
sendFinalCompressed({
|
|
368
|
+
path: compressed,
|
|
369
|
+
duration: raw.duration,
|
|
252
370
|
});
|
|
253
|
-
}
|
|
371
|
+
}, 10);
|
|
254
372
|
});
|
|
255
373
|
}
|
|
256
374
|
});
|
|
@@ -272,10 +390,7 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
|
|
|
272
390
|
export default ImagePicker;
|
|
273
391
|
const styles = StyleSheet.create({
|
|
274
392
|
container: { flex: 1, padding: 16 },
|
|
275
|
-
row: {
|
|
276
|
-
flexDirection: "row",
|
|
277
|
-
alignItems: "center",
|
|
278
|
-
},
|
|
393
|
+
row: { flexDirection: "row", alignItems: "center" },
|
|
279
394
|
text: { ...Typography.style.standardU(), color: Colors.white },
|
|
280
395
|
icon: {
|
|
281
396
|
width: Scale.moderateScale(20),
|
|
@@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
|
|
|
4
4
|
import { Colors, Typography, Scale, Images } from "../../styles";
|
|
5
5
|
import { defaultRegex } from "../../utils/regex";
|
|
6
6
|
import { messages } from "../../constants/messages";
|
|
7
|
-
const Input = ({ intlType, textKey, placeholder, inputPlaceholderTextColor, leftIcon, rightIcon, onLeftIconPress, onRightIconPress, style, containerStyle, inputStyle, errorTextStyle, leftIconStyle, rightIconStyle, leftIconWrapperStyle, rightIconWrapperStyle, type = "text", editable = true, error, value = "", customRegex, customErrorMessage, onChangeText, onValidation, onBlur: restOnBlur, onFocus: restOnFocus, multiline = false, maxLength, returnKeyType, countryCode, onPressCountryCode, countryCodeTextStyle, countryCodeWrapperStyle, ...rest }) => {
|
|
7
|
+
const Input = ({ intlType, textKey, placeholder, inputPlaceholderTextColor, leftIcon, rightIcon, onLeftIconPress, onRightIconPress, style, containerStyle, inputStyle, errorTextStyle, leftIconStyle, rightIconStyle, leftIconWrapperStyle, rightIconWrapperStyle, type = "text", editable = true, error, value = "", customRegex, customErrorMessage, onChangeText, onValidation, onBlur: restOnBlur, onFocus: restOnFocus, multiline = false, maxLength, returnKeyType, countryCode, onPressCountryCode, countryCodeTextStyle, countryCodeWrapperStyle, isError = true, ...rest }) => {
|
|
8
8
|
const { t } = useTranslation();
|
|
9
9
|
const [secureText, setSecureText] = useState(type === "password");
|
|
10
10
|
const [internalError, setInternalError] = useState(null);
|
|
@@ -97,7 +97,7 @@ const Input = ({ intlType, textKey, placeholder, inputPlaceholderTextColor, left
|
|
|
97
97
|
type === "password" ? (React.createElement(TouchableOpacity, { onPress: () => setSecureText(!secureText), style: [styles.iconWrapper, rightIconWrapperStyle] },
|
|
98
98
|
React.createElement(Image, { source: secureText ? Images.Eyeoff : Images.Eyeon, style: [styles.icon, rightIconStyle], resizeMode: "contain" }))) : (rightIcon && (React.createElement(TouchableOpacity, { onPress: onRightIconPress, disabled: !onRightIconPress, style: [styles.iconWrapper, rightIconWrapperStyle] },
|
|
99
99
|
React.createElement(Image, { source: rightIcon, style: [styles.icon, rightIconStyle], resizeMode: "contain" }))))),
|
|
100
|
-
showError && (React.createElement(Text, { style: [styles.errorText, errorTextStyle] }, showError))));
|
|
100
|
+
showError && isError && (React.createElement(Text, { style: [styles.errorText, errorTextStyle] }, showError))));
|
|
101
101
|
};
|
|
102
102
|
export default Input;
|
|
103
103
|
const styles = StyleSheet.create({
|
|
@@ -167,239 +167,3 @@ const styles = StyleSheet.create({
|
|
|
167
167
|
color: Colors.textColor,
|
|
168
168
|
},
|
|
169
169
|
});
|
|
170
|
-
// import React, { useState, useCallback, useEffect } from "react";
|
|
171
|
-
// import {
|
|
172
|
-
// View,
|
|
173
|
-
// TextInput,
|
|
174
|
-
// TextInputProps,
|
|
175
|
-
// StyleSheet,
|
|
176
|
-
// Image,
|
|
177
|
-
// ImageSourcePropType,
|
|
178
|
-
// TouchableOpacity,
|
|
179
|
-
// Text,
|
|
180
|
-
// NativeSyntheticEvent,
|
|
181
|
-
// TextInputFocusEventData,
|
|
182
|
-
// } from "react-native";
|
|
183
|
-
// import { useTranslation } from "react-i18next";
|
|
184
|
-
// import { Colors, Typography, Scale, Images } from "../../styles";
|
|
185
|
-
// import { defaultRegex, InputType } from "../../utils/regex";
|
|
186
|
-
// import { messages } from "../../constants/messages";
|
|
187
|
-
// interface InputProps extends TextInputProps {
|
|
188
|
-
// intlType?: string;
|
|
189
|
-
// textKey?: string;
|
|
190
|
-
// leftIcon?: ImageSourcePropType;
|
|
191
|
-
// rightIcon?: ImageSourcePropType;
|
|
192
|
-
// onLeftIconPress?: () => void;
|
|
193
|
-
// onRightIconPress?: () => void;
|
|
194
|
-
// type?: InputType;
|
|
195
|
-
// editable?: boolean;
|
|
196
|
-
// error?: string | boolean;
|
|
197
|
-
// value?: string;
|
|
198
|
-
// customRegex?: RegExp;
|
|
199
|
-
// customErrorMessage?: string;
|
|
200
|
-
// onChangeText?: (text: string) => void;
|
|
201
|
-
// onValidation?: (errorObj: { type: InputType; hasError: boolean; message?: string }) => void;
|
|
202
|
-
// containerStyle?: object;
|
|
203
|
-
// inputStyle?: object;
|
|
204
|
-
// errorTextStyle?: object;
|
|
205
|
-
// leftIconStyle?: object;
|
|
206
|
-
// rightIconStyle?: object;
|
|
207
|
-
// }
|
|
208
|
-
// const Input: React.FC<InputProps> = ({
|
|
209
|
-
// intlType,
|
|
210
|
-
// textKey,
|
|
211
|
-
// placeholder,
|
|
212
|
-
// leftIcon,
|
|
213
|
-
// rightIcon,
|
|
214
|
-
// onLeftIconPress,
|
|
215
|
-
// onRightIconPress,
|
|
216
|
-
// style,
|
|
217
|
-
// containerStyle,
|
|
218
|
-
// inputStyle,
|
|
219
|
-
// errorTextStyle,
|
|
220
|
-
// leftIconStyle,
|
|
221
|
-
// rightIconStyle,
|
|
222
|
-
// type = "text",
|
|
223
|
-
// editable = true,
|
|
224
|
-
// error,
|
|
225
|
-
// value = "",
|
|
226
|
-
// customRegex,
|
|
227
|
-
// customErrorMessage,
|
|
228
|
-
// onChangeText,
|
|
229
|
-
// onValidation,
|
|
230
|
-
// onBlur: restOnBlur,
|
|
231
|
-
// onFocus: restOnFocus,
|
|
232
|
-
// multiline = false,
|
|
233
|
-
// ...rest
|
|
234
|
-
// }) => {
|
|
235
|
-
// const { t } = useTranslation();
|
|
236
|
-
// const [secureText, setSecureText] = useState(type === "password");
|
|
237
|
-
// const [internalError, setInternalError] = useState<string | null>(null);
|
|
238
|
-
// const [wasBlurred, setWasBlurred] = useState(false);
|
|
239
|
-
// const [isFocused, setIsFocused] = useState(false);
|
|
240
|
-
// const translatedPlaceholder =
|
|
241
|
-
// intlType && textKey ? String(t(textKey, { ns: intlType, value })) : placeholder;
|
|
242
|
-
// const keyboardType: TextInputProps["keyboardType"] =
|
|
243
|
-
// type === "email" ? "email-address" : type === "number" ? "numeric" : "default";
|
|
244
|
-
// const validateInternal = useCallback(
|
|
245
|
-
// (text: string) => {
|
|
246
|
-
// if (!wasBlurred || !editable) return;
|
|
247
|
-
// const regexToUse = customRegex ?? defaultRegex[type];
|
|
248
|
-
// const fallbackMsg = messages.invalid[type] || "Invalid input";
|
|
249
|
-
// const msg = customErrorMessage || fallbackMsg;
|
|
250
|
-
// if (text === "" || !regexToUse.test(text)) {
|
|
251
|
-
// setInternalError(msg);
|
|
252
|
-
// onValidation?.({ type, hasError: true, message: msg });
|
|
253
|
-
// } else {
|
|
254
|
-
// setInternalError(null);
|
|
255
|
-
// onValidation?.({ type, hasError: false });
|
|
256
|
-
// }
|
|
257
|
-
// },
|
|
258
|
-
// [wasBlurred, editable, type, customRegex, customErrorMessage, onValidation]
|
|
259
|
-
// );
|
|
260
|
-
// useEffect(() => {
|
|
261
|
-
// if (!customRegex) return;
|
|
262
|
-
// if (internalError && value && customRegex.test(value)) {
|
|
263
|
-
// setInternalError(null);
|
|
264
|
-
// onValidation?.({ type, hasError: false });
|
|
265
|
-
// }
|
|
266
|
-
// }, [value]);
|
|
267
|
-
// const handleChangeText = (text: string) => {
|
|
268
|
-
// onChangeText?.(text);
|
|
269
|
-
// if (wasBlurred) validateInternal(text);
|
|
270
|
-
// };
|
|
271
|
-
// const handleFocus = (e?: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
272
|
-
// setIsFocused(true);
|
|
273
|
-
// restOnFocus?.(e as any);
|
|
274
|
-
// };
|
|
275
|
-
// const handleBlur = (e?: NativeSyntheticEvent<TextInputFocusEventData>) => {
|
|
276
|
-
// setIsFocused(false);
|
|
277
|
-
// if (!wasBlurred) setWasBlurred(true);
|
|
278
|
-
// if (editable) validateInternal(value || "");
|
|
279
|
-
// restOnBlur?.(e as any);
|
|
280
|
-
// };
|
|
281
|
-
// const rawError = (typeof error === "string" ? error : undefined) ?? internalError ?? null;
|
|
282
|
-
// const showError = (() => {
|
|
283
|
-
// if (!rawError || !editable) return null;
|
|
284
|
-
// return wasBlurred && !isFocused ? rawError : null;
|
|
285
|
-
// })();
|
|
286
|
-
// return (
|
|
287
|
-
// <View style={{ marginBottom: 10 }}>
|
|
288
|
-
// <View
|
|
289
|
-
// style={[
|
|
290
|
-
// styles.container,
|
|
291
|
-
// !editable && styles.disabled,
|
|
292
|
-
// !!showError && styles.errorBorder,
|
|
293
|
-
// isFocused && styles.focusedBorder,
|
|
294
|
-
// style,
|
|
295
|
-
// containerStyle,
|
|
296
|
-
// multiline && styles.multilineContainer,
|
|
297
|
-
// ]}
|
|
298
|
-
// >
|
|
299
|
-
// {leftIcon && (
|
|
300
|
-
// <TouchableOpacity
|
|
301
|
-
// onPress={onLeftIconPress}
|
|
302
|
-
// disabled={!onLeftIconPress}
|
|
303
|
-
// style={styles.iconWrapper}
|
|
304
|
-
// >
|
|
305
|
-
// <Image source={leftIcon} style={[styles.icon, leftIconStyle]} resizeMode="contain" />
|
|
306
|
-
// </TouchableOpacity>
|
|
307
|
-
// )}
|
|
308
|
-
// <TextInput
|
|
309
|
-
// style={[
|
|
310
|
-
// styles.input,
|
|
311
|
-
// !editable && styles.disabledText,
|
|
312
|
-
// multiline && styles.multilineInput,
|
|
313
|
-
// inputStyle,
|
|
314
|
-
// ]}
|
|
315
|
-
// placeholder={translatedPlaceholder}
|
|
316
|
-
// placeholderTextColor={Colors.textGrey}
|
|
317
|
-
// editable={editable}
|
|
318
|
-
// secureTextEntry={secureText}
|
|
319
|
-
// keyboardType={keyboardType}
|
|
320
|
-
// value={value}
|
|
321
|
-
// onChangeText={handleChangeText}
|
|
322
|
-
// onFocus={handleFocus}
|
|
323
|
-
// onBlur={handleBlur}
|
|
324
|
-
// multiline={multiline}
|
|
325
|
-
// {...rest}
|
|
326
|
-
// />
|
|
327
|
-
// {type === "password" ? (
|
|
328
|
-
// <TouchableOpacity onPress={() => setSecureText(!secureText)} style={styles.iconWrapper}>
|
|
329
|
-
// <Image
|
|
330
|
-
// source={secureText ? Images.Eyeoff : Images.Eyeon}
|
|
331
|
-
// style={[styles.icon, rightIconStyle]}
|
|
332
|
-
// resizeMode="contain"
|
|
333
|
-
// />
|
|
334
|
-
// </TouchableOpacity>
|
|
335
|
-
// ) : (
|
|
336
|
-
// rightIcon && (
|
|
337
|
-
// <TouchableOpacity
|
|
338
|
-
// onPress={onRightIconPress}
|
|
339
|
-
// disabled={!onRightIconPress}
|
|
340
|
-
// style={styles.iconWrapper}
|
|
341
|
-
// >
|
|
342
|
-
// <Image source={rightIcon} style={[styles.icon, rightIconStyle]} resizeMode="contain" />
|
|
343
|
-
// </TouchableOpacity>
|
|
344
|
-
// )
|
|
345
|
-
// )}
|
|
346
|
-
// </View>
|
|
347
|
-
// {showError && <Text style={[styles.errorText, errorTextStyle]}>{showError}</Text>}
|
|
348
|
-
// </View>
|
|
349
|
-
// );
|
|
350
|
-
// };
|
|
351
|
-
// export default Input;
|
|
352
|
-
// const styles = StyleSheet.create({
|
|
353
|
-
// container: {
|
|
354
|
-
// flexDirection: "row",
|
|
355
|
-
// alignItems: "center",
|
|
356
|
-
// borderRadius: 5,
|
|
357
|
-
// borderWidth: 1,
|
|
358
|
-
// borderColor: Colors.primaryColor,
|
|
359
|
-
// backgroundColor: Colors.white,
|
|
360
|
-
// height: Scale.moderateScale(50),
|
|
361
|
-
// paddingHorizontal: Scale.moderateScale(4),
|
|
362
|
-
// },
|
|
363
|
-
// multilineContainer: {
|
|
364
|
-
// height: "auto",
|
|
365
|
-
// minHeight: Scale.moderateScale(80),
|
|
366
|
-
// alignItems: "flex-start",
|
|
367
|
-
// paddingVertical: Scale.moderateScale(8),
|
|
368
|
-
// },
|
|
369
|
-
// focusedBorder: {
|
|
370
|
-
// borderColor: Colors.primaryColor,
|
|
371
|
-
// },
|
|
372
|
-
// input: {
|
|
373
|
-
// flex: 1,
|
|
374
|
-
// ...Typography.style.standardU(),
|
|
375
|
-
// textTransform: "none",
|
|
376
|
-
// color: Colors.textColor,
|
|
377
|
-
// },
|
|
378
|
-
// multilineInput: {
|
|
379
|
-
// textAlignVertical: "top",
|
|
380
|
-
// paddingTop: Scale.moderateScale(5),
|
|
381
|
-
// },
|
|
382
|
-
// iconWrapper: {
|
|
383
|
-
// padding: Scale.moderateScale(5),
|
|
384
|
-
// },
|
|
385
|
-
// icon: {
|
|
386
|
-
// width: Scale.moderateScale(20),
|
|
387
|
-
// height: Scale.moderateScale(20),
|
|
388
|
-
// tintColor: Colors.primaryColor,
|
|
389
|
-
// },
|
|
390
|
-
// disabled: {
|
|
391
|
-
// backgroundColor: Colors.lightGrey,
|
|
392
|
-
// borderColor: Colors.borderGrey,
|
|
393
|
-
// },
|
|
394
|
-
// disabledText: {
|
|
395
|
-
// color: Colors.disabledGrey,
|
|
396
|
-
// },
|
|
397
|
-
// errorBorder: {
|
|
398
|
-
// borderColor: Colors.dangerRed,
|
|
399
|
-
// },
|
|
400
|
-
// errorText: {
|
|
401
|
-
// color: Colors.dangerRed,
|
|
402
|
-
// fontSize: Scale.moderateScale(12),
|
|
403
|
-
// marginTop: 3,
|
|
404
|
-
// },
|
|
405
|
-
// });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamnhz/rn-ui-toolkit",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -31,7 +31,9 @@
|
|
|
31
31
|
"react-native-safe-area-context": "^5.6.1",
|
|
32
32
|
"react-native-svg": "^15.12.1",
|
|
33
33
|
"react-native-vector-icons": "^10.3.0",
|
|
34
|
-
"react-native-video-trim": "^3.0.9"
|
|
34
|
+
"react-native-video-trim": "^3.0.9",
|
|
35
|
+
"react-native-compressor": "^1.13.0",
|
|
36
|
+
"react-native-fs": "^2.20.0"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@react-native-documents/picker": "^10.1.5",
|