@teamnhz/rn-ui-toolkit 1.1.7 → 1.1.9

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.
@@ -1,17 +1,2 @@
1
- type AppHeaderProps = {
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.leftImage, leftIconStyle] }))) : null,
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.rightImage, rightImageTwoStyle] }))) : null)),
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
- height: 100,
27
- width: "100%",
28
- },
29
- leftImage: {
30
- height: 44,
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
- rightImage: {
34
- height: 44,
35
- width: 44,
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,17 +2,10 @@ import React from "react";
2
2
  type Props = {
3
3
  mediaType: "photo" | "video";
4
4
  isMultiSelect?: boolean;
5
- onSuccess: (data: any) => void;
5
+ onSuccess: (response: any) => void;
6
6
  visible: boolean;
7
7
  onClose: () => void;
8
8
  enableCompression?: boolean;
9
- imageCompressionOptions?: {
10
- maxWidth?: number;
11
- quality?: number;
12
- };
13
- videoCompressionOptions?: {
14
- compressionMethod?: "auto" | "manual";
15
- };
16
9
  };
17
10
  declare const ImagePicker: React.FC<Props>;
18
11
  export default ImagePicker;
@@ -1,494 +1,127 @@
1
- // // import React, { useCallback } from "react";
2
- // // import {
3
- // // View,
4
- // // Text,
5
- // // TouchableOpacity,
6
- // // StyleSheet,
7
- // // Image,
8
- // // Alert,
9
- // // ViewStyle,
10
- // // TextStyle,
11
- // // ImageStyle,
12
- // // } from "react-native";
13
- // // import { BottomSheet, Dividers } from "../index";
14
- // // import { launchCamera, launchImageLibrary } from "react-native-image-picker";
15
- // // import ImageCropPicker from "react-native-image-crop-picker";
16
- // // import { Colors, Images, Scale, Typography } from "../../styles";
17
- // // type StyleProps = {
18
- // // containerStyle?: ViewStyle;
19
- // // rowStyle?: ViewStyle;
20
- // // textStyle?: TextStyle;
21
- // // iconStyle?: ImageStyle;
22
- // // };
23
- // // type Props = {
24
- // // mediaType: "photo" | "video";
25
- // // isMultiSelect?: boolean;
26
- // // onSuccess: (data: any) => void;
27
- // // visible: boolean;
28
- // // onClose: () => void;
29
- // // } & StyleProps;
30
- // // const ImagePicker: React.FC<Props> = ({
31
- // // mediaType,
32
- // // isMultiSelect = false,
33
- // // onSuccess,
34
- // // visible,
35
- // // onClose,
36
- // // containerStyle,
37
- // // rowStyle,
38
- // // textStyle,
39
- // // iconStyle,
40
- // // }) => {
41
- // // const onComplete = useCallback(
42
- // // (data: any) => {
43
- // // onSuccess(data);
44
- // // onClose();
45
- // // },
46
- // // [onSuccess, onClose]
47
- // // );
48
- // // const onCamera = () => {
49
- // // if (mediaType === "photo") {
50
- // // if (isMultiSelect) {
51
- // // ImageCropPicker.openCamera({ mediaType: "photo" })
52
- // // .then((response) => onComplete([response]))
53
- // // .catch(() => {});
54
- // // } else {
55
- // // ImageCropPicker.openCamera({ mediaType: "photo" })
56
- // // .then((response) =>
57
- // // ImageCropPicker.openCropper({
58
- // // path: response?.path,
59
- // // width: response?.width,
60
- // // height: response?.height,
61
- // // mediaType: "photo",
62
- // // freeStyleCropEnabled: true,
63
- // // }).then(onComplete)
64
- // // )
65
- // // .catch(() => {});
66
- // // }
67
- // // } else {
68
- // // launchCamera({ mediaType: "video" }, (response) => {
69
- // // if (response?.assets) {
70
- // // onComplete({
71
- // // path: response.assets[0]?.uri,
72
- // // duration: response.assets[0]?.duration,
73
- // // });
74
- // // }
75
- // // });
76
- // // }
77
- // // };
78
- // // const onGallery = () => {
79
- // // if (mediaType === "photo") {
80
- // // if (isMultiSelect) {
81
- // // ImageCropPicker.openPicker({
82
- // // mediaType: "photo",
83
- // // multiple: true,
84
- // // maxFiles: 5,
85
- // // })
86
- // // .then((images) => {
87
- // // if (images.length > 5) {
88
- // // Alert.alert(
89
- // // "Limit Exceeded",
90
- // // "You can only select up to 5 images."
91
- // // );
92
- // // } else {
93
- // // onComplete(images);
94
- // // }
95
- // // })
96
- // // .catch(() => {});
97
- // // } else {
98
- // // ImageCropPicker.openPicker({ mediaType: "photo" })
99
- // // .then((response) =>
100
- // // ImageCropPicker.openCropper({
101
- // // path: response?.path,
102
- // // width: response?.width,
103
- // // height: response?.height,
104
- // // mediaType: "photo",
105
- // // freeStyleCropEnabled: true,
106
- // // }).then(onComplete)
107
- // // )
108
- // // .catch(() => {});
109
- // // }
110
- // // } else {
111
- // // launchImageLibrary({ mediaType: "video" }, (result) => {
112
- // // if (result?.assets) {
113
- // // onComplete({
114
- // // path: result.assets[0]?.uri,
115
- // // duration: result.assets[0]?.duration,
116
- // // });
117
- // // }
118
- // // });
119
- // // }
120
- // // };
121
- // // return (
122
- // // <BottomSheet visible={visible} onClose={onClose} height={230}>
123
- // // <View style={[styles.container, containerStyle]}>
124
- // // {/* Camera */}
125
- // // <TouchableOpacity style={[styles.row, rowStyle]} onPress={onCamera}>
126
- // // <Image source={Images.video_icon} style={[styles.icon, iconStyle]} />
127
- // // <Text style={[styles.text, textStyle]}>Camera</Text>
128
- // // </TouchableOpacity>
129
- // // <Dividers small />
130
- // // {/* Gallery */}
131
- // // <TouchableOpacity style={[styles.row, rowStyle]} onPress={onGallery}>
132
- // // <Image source={Images.image_icon} style={[styles.icon, iconStyle]} />
133
- // // <Text style={[styles.text, textStyle]}>Gallery</Text>
134
- // // </TouchableOpacity>
135
- // // <Dividers small />
136
- // // {/* Cancel */}
137
- // // <TouchableOpacity style={[styles.row, rowStyle]} onPress={onClose}>
138
- // // <Image source={Images.cancel} style={[styles.icon, iconStyle]} />
139
- // // <Text style={[styles.text, textStyle]}>Cancel</Text>
140
- // // </TouchableOpacity>
141
- // // </View>
142
- // // </BottomSheet>
143
- // // );
144
- // // };
145
- // // export default ImagePicker;
146
- // // const styles = StyleSheet.create({
147
- // // container: { flex: 1, padding: 16 },
148
- // // row: {
149
- // // flexDirection: "row",
150
- // // alignItems: "center",
151
- // // },
152
- // // text: { ...Typography.style.standardU(), color: Colors.white },
153
- // // icon: {
154
- // // width: Scale.moderateScale(20),
155
- // // height: Scale.moderateScale(20),
156
- // // marginRight: 10,
157
- // // tintColor: Colors.white,
158
- // // },
159
- // // });
160
- // import React, { useCallback } from "react";
161
- // import {
162
- // View,
163
- // Text,
164
- // TouchableOpacity,
165
- // StyleSheet,
166
- // Image,
167
- // Alert,
168
- // } from "react-native";
169
- // import { BottomSheet, Dividers } from "../index";
170
- // import { launchCamera, launchImageLibrary } from "react-native-image-picker";
171
- // import ImageCropPicker from "react-native-image-crop-picker";
172
- // import { Colors, Images, Scale, Typography } from "../../styles";
173
- // // 👇 Import your permission utilities
174
- // import {
175
- // cameraPermissions,
176
- // galleryPermissions,
177
- // } from "../../utils/permissions"; // ✅ Make sure path is correct
178
- // type Props = {
179
- // mediaType: "photo" | "video";
180
- // isMultiSelect?: boolean;
181
- // onSuccess: (data: any) => void;
182
- // visible: boolean;
183
- // onClose: () => void;
184
- // };
185
- // const ImagePicker: React.FC<Props> = ({
186
- // mediaType,
187
- // isMultiSelect = false,
188
- // onSuccess,
189
- // visible,
190
- // onClose,
191
- // }) => {
192
- // // ✅ Success handler
193
- // const onComplete = useCallback(
194
- // (data: any) => {
195
- // onSuccess(data);
196
- // onClose();
197
- // },
198
- // [onSuccess, onClose]
199
- // );
200
- // // ✅ CAMERA HANDLER with permission check
201
- // const handleCamera = async () => {
202
- // await cameraPermissions(async (granted: boolean) => {
203
- // if (!granted) return; // ❌ Permission denied, handled by your alert
204
- // if (mediaType === "photo") {
205
- // if (isMultiSelect) {
206
- // // single capture, multiple not supported directly from camera
207
- // ImageCropPicker.openCamera({ mediaType: "photo" })
208
- // .then((response) => onComplete([response]))
209
- // .catch(() => {});
210
- // } else {
211
- // ImageCropPicker.openCamera({ mediaType: "photo" })
212
- // .then((response) =>
213
- // ImageCropPicker.openCropper({
214
- // path: response?.path,
215
- // width: response?.width,
216
- // height: response?.height,
217
- // mediaType: "photo",
218
- // freeStyleCropEnabled: true,
219
- // }).then(onComplete)
220
- // )
221
- // .catch(() => {});
222
- // }
223
- // } else {
224
- // // 🎥 VIDEO CAPTURE
225
- // launchCamera({ mediaType: "video" }, (response) => {
226
- // if (response?.assets?.length) {
227
- // onComplete({
228
- // path: response.assets[0]?.uri,
229
- // duration: response.assets[0]?.duration,
230
- // });
231
- // }
232
- // });
233
- // }
234
- // });
235
- // };
236
- // // ✅ GALLERY HANDLER with permission check
237
- // const handleGallery = async () => {
238
- // await galleryPermissions(async (granted: boolean) => {
239
- // if (!granted) return; // ❌ Permission denied, handled by your alert
240
- // if (mediaType === "photo") {
241
- // if (isMultiSelect) {
242
- // ImageCropPicker.openPicker({
243
- // mediaType: "photo",
244
- // multiple: true,
245
- // maxFiles: 5,
246
- // })
247
- // .then((images) => {
248
- // if (images.length > 5) {
249
- // Alert.alert(
250
- // "Limit Exceeded",
251
- // "You can only select up to 5 images."
252
- // );
253
- // } else {
254
- // onComplete(images);
255
- // }
256
- // })
257
- // .catch(() => {});
258
- // } else {
259
- // ImageCropPicker.openPicker({ mediaType: "photo" })
260
- // .then((response) =>
261
- // ImageCropPicker.openCropper({
262
- // path: response?.path,
263
- // width: response?.width,
264
- // height: response?.height,
265
- // mediaType: "photo",
266
- // freeStyleCropEnabled: true,
267
- // }).then(onComplete)
268
- // )
269
- // .catch(() => {});
270
- // }
271
- // } else {
272
- // // 🎥 VIDEO FROM GALLERY
273
- // launchImageLibrary({ mediaType: "video" }, (result) => {
274
- // if (result?.assets?.length) {
275
- // onComplete({
276
- // path: result.assets[0]?.uri,
277
- // duration: result.assets[0]?.duration,
278
- // });
279
- // }
280
- // });
281
- // }
282
- // });
283
- // };
284
- // return (
285
- // <BottomSheet visible={visible} onClose={onClose} height={230}>
286
- // <View style={styles.container}>
287
- // {/* 📸 Camera */}
288
- // <TouchableOpacity style={styles.row} onPress={handleCamera}>
289
- // <Image source={Images.video_icon} style={styles.icon} />
290
- // <Text style={styles.text}>Camera</Text>
291
- // </TouchableOpacity>
292
- // <Dividers small />
293
- // {/* 🖼️ Gallery */}
294
- // <TouchableOpacity style={styles.row} onPress={handleGallery}>
295
- // <Image source={Images.image_icon} style={styles.icon} />
296
- // <Text style={styles.text}>Gallery</Text>
297
- // </TouchableOpacity>
298
- // <Dividers small />
299
- // {/* ❌ Cancel */}
300
- // <TouchableOpacity style={styles.row} onPress={onClose}>
301
- // <Image source={Images.cancel} style={styles.icon} />
302
- // <Text style={styles.text}>Cancel</Text>
303
- // </TouchableOpacity>
304
- // </View>
305
- // </BottomSheet>
306
- // );
307
- // };
308
- // export default ImagePicker;
309
- // const styles = StyleSheet.create({
310
- // container: { flex: 1, padding: 16 },
311
- // row: {
312
- // flexDirection: "row",
313
- // alignItems: "center",
314
- // },
315
- // text: { ...Typography.style.standardU(), color: Colors.white },
316
- // icon: {
317
- // width: Scale.moderateScale(20),
318
- // height: Scale.moderateScale(20),
319
- // marginRight: 10,
320
- // tintColor: Colors.white,
321
- // },
322
- // });
323
- import React, { useCallback } from "react";
324
- import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, } from "react-native";
1
+ import React from "react";
2
+ import { View, Text, TouchableOpacity, StyleSheet, Image, } from "react-native";
325
3
  import { BottomSheet, Dividers } from "../index";
326
4
  import { launchCamera, launchImageLibrary } from "react-native-image-picker";
327
5
  import ImageCropPicker from "react-native-image-crop-picker";
328
6
  import { Image as ImageCompressor, Video as VideoCompressor, } from "react-native-compressor";
329
- import RNFS from "react-native-fs";
330
7
  import { Colors, Images, Scale, Typography } from "../../styles";
331
- // Import your permission utilities
332
8
  import { cameraPermissions, galleryPermissions, checkMicroPhonePermission, } from "../../utils/permissions";
333
- const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, enableCompression = true, imageCompressionOptions = { maxWidth: 1080, quality: 0.7 }, videoCompressionOptions = { compressionMethod: "auto" }, }) => {
334
- // Success handler
335
- const onComplete = useCallback((data) => {
336
- onSuccess(data);
9
+ // NORMALIZE FINAL RESPONSE
10
+ const buildResponse = (raw, compressedPath) => {
11
+ return {
12
+ path: compressedPath || raw?.path || raw?.uri?.replace("file://", ""),
13
+ originalPath: raw?.path?.replace("file://", "") ||
14
+ raw?.uri?.replace("file://", "") ||
15
+ "",
16
+ fileName: raw?.fileName || "",
17
+ type: raw?.type || "",
18
+ duration: raw?.duration || undefined,
19
+ };
20
+ };
21
+ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, enableCompression = true, }) => {
22
+ // SEND RAW (loading true) TO PARENT
23
+ const sendRaw = (raw) => {
337
24
  onClose();
338
- }, [onSuccess, onClose]);
339
- // Helper: Compress Image
340
- const compressImage = async (imagePath) => {
341
- if (!enableCompression)
342
- return imagePath;
25
+ onSuccess({
26
+ loading: true,
27
+ data: buildResponse(raw),
28
+ });
29
+ };
30
+ // SEND FINAL COMPRESSED
31
+ const sendFinal = (raw, compressedPath) => {
32
+ onSuccess({
33
+ loading: false,
34
+ data: buildResponse(raw, compressedPath),
35
+ });
36
+ };
37
+ // IMAGE COMPRESSOR
38
+ const compressImage = async (path) => {
343
39
  try {
344
- const compressedImage = await ImageCompressor.compress(imagePath, {
345
- maxWidth: imageCompressionOptions.maxWidth,
346
- quality: imageCompressionOptions.quality,
40
+ const compressed = await ImageCompressor.compress(path, {
41
+ maxWidth: 1080,
42
+ quality: 0.7,
347
43
  });
348
- // Log compressed size (optional)
349
- const stats = await RNFS.stat(compressedImage.replace("file://", ""));
350
- const sizeInMB = stats.size / (1024 * 1024);
351
- console.log("Compressed Image Size (MB):", sizeInMB);
352
- return compressedImage;
44
+ return compressed;
353
45
  }
354
- catch (error) {
355
- console.error("Image compression error:", error);
356
- return imagePath; // Return original if compression fails
46
+ catch {
47
+ return path;
357
48
  }
358
49
  };
359
- // Helper: Compress Video
360
- const compressVideo = async (videoUri) => {
361
- if (!enableCompression)
362
- return videoUri;
50
+ // VIDEO COMPRESSOR
51
+ const compressVideo = async (uri) => {
363
52
  try {
364
- const compressedVideo = await VideoCompressor.compress(videoUri, {
365
- compressionMethod: videoCompressionOptions.compressionMethod,
53
+ const compressed = await VideoCompressor.compress(uri, {
54
+ compressionMethod: "auto",
366
55
  });
367
- // Log compressed size (optional)
368
- const stats = await RNFS.stat(compressedVideo.replace("file://", ""));
369
- const sizeInMB = stats.size / (1024 * 1024);
370
- console.log("Compressed Video Size (MB):", sizeInMB);
371
- return compressedVideo;
56
+ return compressed;
372
57
  }
373
- catch (error) {
374
- console.error("Video compression error:", error);
375
- return videoUri; // Return original if compression fails
58
+ catch {
59
+ return uri;
376
60
  }
377
61
  };
378
- // CAMERA HANDLER with permission check and compression
62
+ // ------------------ CAMERA ------------------
379
63
  const handleCamera = async () => {
380
64
  await cameraPermissions(async (granted) => {
381
65
  if (!granted)
382
66
  return;
383
67
  if (mediaType === "photo") {
384
68
  try {
385
- const response = await ImageCropPicker.openCamera({
386
- mediaType: "photo",
387
- });
388
- if (isMultiSelect) {
389
- // Single capture for multi-select mode
390
- const compressedPath = await compressImage(response.path);
391
- onComplete([{ ...response, path: compressedPath }]);
392
- }
393
- else {
394
- // Crop and compress single image
395
- const cropped = await ImageCropPicker.openCropper({
396
- path: response?.path,
397
- width: response?.width,
398
- height: response?.height,
399
- mediaType: "photo",
400
- freeStyleCropEnabled: true,
401
- });
402
- const compressedPath = await compressImage(cropped.path);
403
- onComplete({ ...cropped, path: compressedPath });
404
- }
69
+ const picked = await ImageCropPicker.openCamera({ mediaType: "photo" });
70
+ sendRaw(picked);
71
+ const compressed = enableCompression
72
+ ? await compressImage(picked.path)
73
+ : picked.path;
74
+ sendFinal(picked, compressed);
405
75
  }
406
- catch (error) {
407
- console.log("Camera cancelled or error:", error);
76
+ catch (e) {
77
+ console.log(e);
408
78
  }
409
79
  }
410
80
  else {
411
- // VIDEO CAPTURE - check microphone permission first
412
- const micPermission = await checkMicroPhonePermission();
413
- if (!micPermission) {
414
- Alert.alert("Microphone Permission Required", "Please enable microphone access to record videos.");
81
+ const mic = await checkMicroPhonePermission();
82
+ if (!mic)
415
83
  return;
416
- }
417
- launchCamera({
418
- mediaType: "video",
419
- durationLimit: 60,
420
- videoQuality: "high",
421
- }, async (response) => {
422
- if (response?.assets?.length) {
423
- const videoUri = response.assets[0]?.uri;
424
- const compressedPath = await compressVideo(videoUri);
425
- onComplete({
426
- path: compressedPath,
427
- duration: response.assets[0]?.duration,
428
- });
429
- }
84
+ launchCamera({ mediaType: "video", durationLimit: 60, videoQuality: "high" }, async (res) => {
85
+ if (!res?.assets?.length)
86
+ return;
87
+ const raw = res.assets[0];
88
+ sendRaw(raw);
89
+ const compressed = enableCompression
90
+ ? await compressVideo(raw.uri)
91
+ : raw.uri;
92
+ sendFinal(raw, compressed);
430
93
  });
431
94
  }
432
95
  });
433
96
  };
434
- // GALLERY HANDLER with permission check and compression
97
+ // ------------------ GALLERY ------------------
435
98
  const handleGallery = async () => {
436
99
  await galleryPermissions(async (granted) => {
437
100
  if (!granted)
438
101
  return;
439
102
  if (mediaType === "photo") {
440
103
  try {
441
- if (isMultiSelect) {
442
- const images = await ImageCropPicker.openPicker({
443
- mediaType: "photo",
444
- multiple: true,
445
- maxFiles: 5,
446
- });
447
- if (images.length > 5) {
448
- Alert.alert("Limit Exceeded", "You can only select up to 5 images.");
449
- return;
450
- }
451
- // Compress multiple images
452
- const compressedImages = await Promise.all(images.map(async (img) => ({
453
- ...img,
454
- path: await compressImage(img.path),
455
- })));
456
- onComplete(compressedImages);
457
- }
458
- else {
459
- // Single image with crop
460
- const response = await ImageCropPicker.openPicker({
461
- mediaType: "photo",
462
- });
463
- const cropped = await ImageCropPicker.openCropper({
464
- path: response?.path,
465
- width: response?.width,
466
- height: response?.height,
467
- mediaType: "photo",
468
- freeStyleCropEnabled: true,
469
- });
470
- const compressedPath = await compressImage(cropped.path);
471
- onComplete({ ...cropped, path: compressedPath });
472
- }
104
+ const picked = await ImageCropPicker.openPicker({ mediaType: "photo" });
105
+ sendRaw(picked);
106
+ const compressed = enableCompression
107
+ ? await compressImage(picked.path)
108
+ : picked.path;
109
+ sendFinal(picked, compressed);
473
110
  }
474
- catch (error) {
475
- console.log("Gallery cancelled or error:", error);
111
+ catch (e) {
112
+ console.log(e);
476
113
  }
477
114
  }
478
115
  else {
479
- // VIDEO FROM GALLERY
480
- launchImageLibrary({
481
- mediaType: "video",
482
- assetRepresentationMode: "current",
483
- }, async (result) => {
484
- if (result?.assets?.length) {
485
- const videoUri = result.assets[0]?.uri;
486
- const compressedPath = await compressVideo(videoUri);
487
- onComplete({
488
- path: compressedPath,
489
- duration: result.assets[0]?.duration,
490
- });
491
- }
116
+ launchImageLibrary({ mediaType: "video" }, async (res) => {
117
+ if (!res?.assets?.length)
118
+ return;
119
+ const raw = res.assets[0];
120
+ sendRaw(raw);
121
+ const compressed = enableCompression
122
+ ? await compressVideo(raw.uri)
123
+ : raw.uri;
124
+ sendFinal(raw, compressed);
492
125
  });
493
126
  }
494
127
  });
@@ -510,10 +143,7 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
510
143
  export default ImagePicker;
511
144
  const styles = StyleSheet.create({
512
145
  container: { flex: 1, padding: 16 },
513
- row: {
514
- flexDirection: "row",
515
- alignItems: "center",
516
- },
146
+ row: { flexDirection: "row", alignItems: "center" },
517
147
  text: { ...Typography.style.standardU(), color: Colors.white },
518
148
  icon: {
519
149
  width: Scale.moderateScale(20),
@@ -35,6 +35,7 @@ interface InputProps extends TextInputProps {
35
35
  onPressCountryCode?: () => void;
36
36
  countryCodeWrapperStyle?: object;
37
37
  countryCodeTextStyle?: object;
38
+ isError?: boolean;
38
39
  }
39
40
  declare const Input: React.FC<InputProps>;
40
41
  export default Input;
@@ -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.7",
3
+ "version": "1.1.9",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [