@teamnhz/rn-ui-toolkit 1.2.8 → 1.3.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.
@@ -5,6 +5,7 @@ interface AppBottomSheetProps {
5
5
  onClose: () => void;
6
6
  children: React.ReactNode;
7
7
  height?: number;
8
+ sheetBackground?: string;
8
9
  }
9
10
  declare const BottomSheet: React.FC<AppBottomSheetProps>;
10
11
  export default BottomSheet;
@@ -1,10 +1,13 @@
1
1
  import React from "react";
2
2
  import { Modal, View, StyleSheet, TouchableOpacity } from "react-native";
3
3
  import { Colors } from "../../styles";
4
- const BottomSheet = ({ ref, visible, onClose, children, height = 300, }) => {
4
+ const BottomSheet = ({ ref, visible, onClose, children, height = 300, sheetBackground = Colors.appThemeColor, }) => {
5
5
  return (React.createElement(Modal, { ref: ref, transparent: true, visible: visible, animationType: "slide" },
6
6
  React.createElement(TouchableOpacity, { style: styles.overlay, activeOpacity: 1, onPress: onClose }),
7
- React.createElement(View, { style: [styles.sheet, { height }] },
7
+ React.createElement(View, { style: [
8
+ styles.sheet,
9
+ { height, backgroundColor: sheetBackground }, // 🔥 dynamic bg color
10
+ ] },
8
11
  React.createElement(View, { style: styles.dragIndicator }),
9
12
  children)));
10
13
  };
@@ -15,7 +18,6 @@ const styles = StyleSheet.create({
15
18
  backgroundColor: "rgba(0,0,0,0.4)",
16
19
  },
17
20
  sheet: {
18
- backgroundColor: Colors.appThemeColor,
19
21
  borderTopLeftRadius: 16,
20
22
  borderTopRightRadius: 16,
21
23
  padding: 16,
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { ViewStyle } from "react-native";
2
3
  type Props = {
3
4
  mediaType: "photo" | "video";
4
5
  isMultiSelect?: boolean;
@@ -13,6 +14,10 @@ type Props = {
13
14
  videoCompressionOptions?: {
14
15
  compressionMethod?: "auto" | "manual";
15
16
  };
17
+ customStyle?: ViewStyle;
18
+ sheetBackgroundColor?: string;
19
+ textColor?: string;
20
+ iconTintColor?: string;
16
21
  };
17
22
  declare const ImagePicker: React.FC<Props>;
18
23
  export default ImagePicker;
@@ -320,23 +320,280 @@
320
320
  // tintColor: Colors.white,
321
321
  // },
322
322
  // });
323
+ // import React, { useCallback } from "react";
324
+ // import {
325
+ // View,
326
+ // Text,
327
+ // TouchableOpacity,
328
+ // StyleSheet,
329
+ // Image,
330
+ // Alert,
331
+ // ViewStyle,
332
+ // } from "react-native";
333
+ // import { BottomSheet, Dividers } from "../index";
334
+ // import { launchCamera, launchImageLibrary } from "react-native-image-picker";
335
+ // import ImageCropPicker from "react-native-image-crop-picker";
336
+ // import {
337
+ // Image as ImageCompressor,
338
+ // Video as VideoCompressor,
339
+ // } from "react-native-compressor";
340
+ // import RNFS from "react-native-fs";
341
+ // import { Colors, Images, Scale, Typography } from "../../styles";
342
+ // // Import your permission utilities
343
+ // import {
344
+ // cameraPermissions,
345
+ // galleryPermissions,
346
+ // checkMicroPhonePermission,
347
+ // } from "../../utils/permissions";
348
+ // type Props = {
349
+ // mediaType: "photo" | "video";
350
+ // isMultiSelect?: boolean;
351
+ // onSuccess: (data: any) => void;
352
+ // visible: boolean;
353
+ // onClose: () => void;
354
+ // // Compression options
355
+ // enableCompression?: boolean;
356
+ // imageCompressionOptions?: {
357
+ // maxWidth?: number;
358
+ // quality?: number;
359
+ // };
360
+ // videoCompressionOptions?: {
361
+ // compressionMethod?: "auto" | "manual";
362
+ // };
363
+ // customStyle?:ViewStyle
364
+ // };
365
+ // const ImagePicker: React.FC<Props> = ({
366
+ // mediaType,
367
+ // isMultiSelect = false,
368
+ // onSuccess,
369
+ // visible,
370
+ // onClose,
371
+ // enableCompression = true,
372
+ // imageCompressionOptions = { maxWidth: 1080, quality: 0.7 },
373
+ // videoCompressionOptions = { compressionMethod: "auto" },
374
+ // customStyle
375
+ // }) => {
376
+ // // Success handler
377
+ // const onComplete = useCallback(
378
+ // (data: any) => {
379
+ // onSuccess(data);
380
+ // onClose();
381
+ // },
382
+ // [onSuccess, onClose]
383
+ // );
384
+ // // Helper: Compress Image
385
+ // const compressImage = async (imagePath: string) => {
386
+ // if (!enableCompression) return imagePath;
387
+ // try {
388
+ // const compressedImage = await ImageCompressor.compress(imagePath, {
389
+ // maxWidth: imageCompressionOptions.maxWidth,
390
+ // quality: imageCompressionOptions.quality,
391
+ // });
392
+ // // Log compressed size (optional)
393
+ // const stats = await RNFS.stat(compressedImage.replace("file://", ""));
394
+ // const sizeInMB = stats.size / (1024 * 1024);
395
+ // console.log("Compressed Image Size (MB):", sizeInMB);
396
+ // return compressedImage;
397
+ // } catch (error) {
398
+ // console.error("Image compression error:", error);
399
+ // return imagePath; // Return original if compression fails
400
+ // }
401
+ // };
402
+ // // Helper: Compress Video
403
+ // const compressVideo = async (videoUri: string) => {
404
+ // if (!enableCompression) return videoUri;
405
+ // try {
406
+ // const compressedVideo = await VideoCompressor.compress(videoUri, {
407
+ // compressionMethod: videoCompressionOptions.compressionMethod,
408
+ // });
409
+ // // Log compressed size (optional)
410
+ // const stats = await RNFS.stat(compressedVideo.replace("file://", ""));
411
+ // const sizeInMB = stats.size / (1024 * 1024);
412
+ // console.log("Compressed Video Size (MB):", sizeInMB);
413
+ // return compressedVideo;
414
+ // } catch (error) {
415
+ // console.error("Video compression error:", error);
416
+ // return videoUri; // Return original if compression fails
417
+ // }
418
+ // };
419
+ // // CAMERA HANDLER with permission check and compression
420
+ // const handleCamera = async () => {
421
+ // await cameraPermissions(async (granted: boolean) => {
422
+ // if (!granted) return;
423
+ // if (mediaType === "photo") {
424
+ // try {
425
+ // const response = await ImageCropPicker.openCamera({
426
+ // mediaType: "photo",
427
+ // });
428
+ // if (isMultiSelect) {
429
+ // // Single capture for multi-select mode
430
+ // const compressedPath = await compressImage(response.path);
431
+ // onComplete([{ ...response, path: compressedPath }]);
432
+ // } else {
433
+ // // Crop and compress single image
434
+ // const cropped = await ImageCropPicker.openCropper({
435
+ // path: response?.path,
436
+ // width: response?.width,
437
+ // height: response?.height,
438
+ // mediaType: "photo",
439
+ // freeStyleCropEnabled: true,
440
+ // });
441
+ // const compressedPath = await compressImage(cropped.path);
442
+ // onComplete({ ...cropped, path: compressedPath });
443
+ // }
444
+ // } catch (error) {
445
+ // console.log("Camera cancelled or error:", error);
446
+ // }
447
+ // } else {
448
+ // // VIDEO CAPTURE - check microphone permission first
449
+ // const micPermission = await checkMicroPhonePermission();
450
+ // if (!micPermission) {
451
+ // Alert.alert(
452
+ // "Microphone Permission Required",
453
+ // "Please enable microphone access to record videos."
454
+ // );
455
+ // return;
456
+ // }
457
+ // launchCamera(
458
+ // {
459
+ // mediaType: "video",
460
+ // durationLimit: 60,
461
+ // videoQuality: "high",
462
+ // },
463
+ // async (response) => {
464
+ // if (response?.assets?.length) {
465
+ // const videoUri = response.assets[0]?.uri;
466
+ // const compressedPath = await compressVideo(videoUri);
467
+ // onComplete({
468
+ // path: compressedPath,
469
+ // duration: response.assets[0]?.duration,
470
+ // });
471
+ // }
472
+ // }
473
+ // );
474
+ // }
475
+ // });
476
+ // };
477
+ // // GALLERY HANDLER with permission check and compression
478
+ // const handleGallery = async () => {
479
+ // await galleryPermissions(async (granted: boolean) => {
480
+ // if (!granted) return;
481
+ // if (mediaType === "photo") {
482
+ // try {
483
+ // if (isMultiSelect) {
484
+ // const images = await ImageCropPicker.openPicker({
485
+ // mediaType: "photo",
486
+ // multiple: true,
487
+ // maxFiles: 5,
488
+ // });
489
+ // if (images.length > 5) {
490
+ // Alert.alert(
491
+ // "Limit Exceeded",
492
+ // "You can only select up to 5 images."
493
+ // );
494
+ // return;
495
+ // }
496
+ // // Compress multiple images
497
+ // const compressedImages = await Promise.all(
498
+ // images.map(async (img) => ({
499
+ // ...img,
500
+ // path: await compressImage(img.path),
501
+ // }))
502
+ // );
503
+ // onComplete(compressedImages);
504
+ // } else {
505
+ // // Single image with crop
506
+ // const response = await ImageCropPicker.openPicker({
507
+ // mediaType: "photo",
508
+ // });
509
+ // const cropped = await ImageCropPicker.openCropper({
510
+ // path: response?.path,
511
+ // width: response?.width,
512
+ // height: response?.height,
513
+ // mediaType: "photo",
514
+ // freeStyleCropEnabled: true,
515
+ // });
516
+ // const compressedPath = await compressImage(cropped.path);
517
+ // onComplete({ ...cropped, path: compressedPath });
518
+ // }
519
+ // } catch (error) {
520
+ // console.log("Gallery cancelled or error:", error);
521
+ // }
522
+ // } else {
523
+ // // VIDEO FROM GALLERY
524
+ // launchImageLibrary(
525
+ // {
526
+ // mediaType: "video",
527
+ // assetRepresentationMode: "current",
528
+ // },
529
+ // async (result) => {
530
+ // if (result?.assets?.length) {
531
+ // const videoUri = result.assets[0]?.uri;
532
+ // const compressedPath = await compressVideo(videoUri);
533
+ // onComplete({
534
+ // path: compressedPath,
535
+ // duration: result.assets[0]?.duration,
536
+ // });
537
+ // }
538
+ // }
539
+ // );
540
+ // }
541
+ // });
542
+ // };
543
+ // return (
544
+ // <BottomSheet visible={visible} onClose={onClose} height={230} sheetBackground={customStyle?.backgroundColor}>
545
+ // <View style={styles.container}>
546
+ // {/* Camera */}
547
+ // <TouchableOpacity style={styles.row} onPress={handleCamera}>
548
+ // <Image source={Images.video_icon} style={styles.icon} />
549
+ // <Text style={styles.text}>Camera</Text>
550
+ // </TouchableOpacity>
551
+ // <Dividers small />
552
+ // {/* Gallery */}
553
+ // <TouchableOpacity style={styles.row} onPress={handleGallery}>
554
+ // <Image source={Images.image_icon} style={styles.icon} />
555
+ // <Text style={styles.text}>Gallery</Text>
556
+ // </TouchableOpacity>
557
+ // <Dividers small />
558
+ // {/* Cancel */}
559
+ // <TouchableOpacity style={styles.row} onPress={onClose}>
560
+ // <Image source={Images.cancel} style={styles.icon} />
561
+ // <Text style={styles.text}>Cancel</Text>
562
+ // </TouchableOpacity>
563
+ // </View>
564
+ // </BottomSheet>
565
+ // );
566
+ // };
567
+ // export default ImagePicker;
568
+ // const styles = StyleSheet.create({
569
+ // container: { flex: 1, padding: 16 },
570
+ // row: {
571
+ // flexDirection: "row",
572
+ // alignItems: "center",
573
+ // },
574
+ // text: { ...Typography.style.standardU(), color: Colors.white },
575
+ // icon: {
576
+ // width: Scale.moderateScale(20),
577
+ // height: Scale.moderateScale(20),
578
+ // marginRight: 10,
579
+ // tintColor: Colors.white,
580
+ // },
581
+ // });
323
582
  import React, { useCallback } from "react";
324
583
  import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, } from "react-native";
325
584
  import { BottomSheet, Dividers } from "../index";
326
585
  import { launchCamera, launchImageLibrary } from "react-native-image-picker";
327
586
  import ImageCropPicker from "react-native-image-crop-picker";
328
587
  import { Image as ImageCompressor, Video as VideoCompressor, } from "react-native-compressor";
329
- import RNFS from "react-native-fs";
330
588
  import { Colors, Images, Scale, Typography } from "../../styles";
331
- // Import your permission utilities
589
+ // Permissions
332
590
  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
591
+ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onClose, enableCompression = true, imageCompressionOptions = { maxWidth: 1080, quality: 0.7 }, videoCompressionOptions = { compressionMethod: "auto" }, customStyle, sheetBackgroundColor, textColor, iconTintColor, }) => {
335
592
  const onComplete = useCallback((data) => {
336
593
  onSuccess(data);
337
594
  onClose();
338
595
  }, [onSuccess, onClose]);
339
- // Helper: Compress Image
596
+ // Compress Image
340
597
  const compressImage = async (imagePath) => {
341
598
  if (!enableCompression)
342
599
  return imagePath;
@@ -345,18 +602,14 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
345
602
  maxWidth: imageCompressionOptions.maxWidth,
346
603
  quality: imageCompressionOptions.quality,
347
604
  });
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
605
  return compressedImage;
353
606
  }
354
607
  catch (error) {
355
608
  console.error("Image compression error:", error);
356
- return imagePath; // Return original if compression fails
609
+ return imagePath;
357
610
  }
358
611
  };
359
- // Helper: Compress Video
612
+ // Compress Video
360
613
  const compressVideo = async (videoUri) => {
361
614
  if (!enableCompression)
362
615
  return videoUri;
@@ -364,34 +617,26 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
364
617
  const compressedVideo = await VideoCompressor.compress(videoUri, {
365
618
  compressionMethod: videoCompressionOptions.compressionMethod,
366
619
  });
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
620
  return compressedVideo;
372
621
  }
373
622
  catch (error) {
374
623
  console.error("Video compression error:", error);
375
- return videoUri; // Return original if compression fails
624
+ return videoUri;
376
625
  }
377
626
  };
378
- // CAMERA HANDLER with permission check and compression
627
+ // Camera
379
628
  const handleCamera = async () => {
380
629
  await cameraPermissions(async (granted) => {
381
630
  if (!granted)
382
631
  return;
383
632
  if (mediaType === "photo") {
384
633
  try {
385
- const response = await ImageCropPicker.openCamera({
386
- mediaType: "photo",
387
- });
634
+ const response = await ImageCropPicker.openCamera({ mediaType: "photo" });
388
635
  if (isMultiSelect) {
389
- // Single capture for multi-select mode
390
- const compressedPath = await compressImage(response.path);
391
- onComplete([{ ...response, path: compressedPath }]);
636
+ const comp = await compressImage(response.path);
637
+ onComplete([{ ...response, path: comp }]);
392
638
  }
393
639
  else {
394
- // Crop and compress single image
395
640
  const cropped = await ImageCropPicker.openCropper({
396
641
  path: response?.path,
397
642
  width: response?.width,
@@ -399,39 +644,31 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
399
644
  mediaType: "photo",
400
645
  freeStyleCropEnabled: true,
401
646
  });
402
- const compressedPath = await compressImage(cropped.path);
403
- onComplete({ ...cropped, path: compressedPath });
647
+ const comp = await compressImage(cropped.path);
648
+ onComplete({ ...cropped, path: comp });
404
649
  }
405
650
  }
406
- catch (error) {
407
- console.log("Camera cancelled or error:", error);
651
+ catch (err) {
652
+ console.log("Camera error:", err);
408
653
  }
409
654
  }
410
655
  else {
411
- // VIDEO CAPTURE - check microphone permission first
412
656
  const micPermission = await checkMicroPhonePermission();
413
657
  if (!micPermission) {
414
658
  Alert.alert("Microphone Permission Required", "Please enable microphone access to record videos.");
415
659
  return;
416
660
  }
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
- });
661
+ launchCamera({ mediaType: "video", durationLimit: 60, videoQuality: "high" }, async (res) => {
662
+ if (res?.assets?.length) {
663
+ const uri = res.assets[0].uri;
664
+ const comp = await compressVideo(uri);
665
+ onComplete({ path: comp, duration: res.assets[0].duration });
429
666
  }
430
667
  });
431
668
  }
432
669
  });
433
670
  };
434
- // GALLERY HANDLER with permission check and compression
671
+ // Gallery
435
672
  const handleGallery = async () => {
436
673
  await galleryPermissions(async (granted) => {
437
674
  if (!granted)
@@ -445,18 +682,16 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
445
682
  maxFiles: 5,
446
683
  });
447
684
  if (images.length > 5) {
448
- Alert.alert("Limit Exceeded", "You can only select up to 5 images.");
685
+ Alert.alert("Limit Exceeded", "You can select up to 5 images.");
449
686
  return;
450
687
  }
451
- // Compress multiple images
452
- const compressedImages = await Promise.all(images.map(async (img) => ({
688
+ const compressed = await Promise.all(images.map(async (img) => ({
453
689
  ...img,
454
690
  path: await compressImage(img.path),
455
691
  })));
456
- onComplete(compressedImages);
692
+ onComplete(compressed);
457
693
  }
458
694
  else {
459
- // Single image with crop
460
695
  const response = await ImageCropPicker.openPicker({
461
696
  mediaType: "photo",
462
697
  });
@@ -467,45 +702,40 @@ const ImagePicker = ({ mediaType, isMultiSelect = false, onSuccess, visible, onC
467
702
  mediaType: "photo",
468
703
  freeStyleCropEnabled: true,
469
704
  });
470
- const compressedPath = await compressImage(cropped.path);
471
- onComplete({ ...cropped, path: compressedPath });
705
+ const comp = await compressImage(cropped.path);
706
+ onComplete({ ...cropped, path: comp });
472
707
  }
473
708
  }
474
- catch (error) {
475
- console.log("Gallery cancelled or error:", error);
709
+ catch (err) {
710
+ console.log("Gallery error:", err);
476
711
  }
477
712
  }
478
713
  else {
479
- // VIDEO FROM GALLERY
480
- launchImageLibrary({
481
- mediaType: "video",
482
- assetRepresentationMode: "current",
483
- }, async (result) => {
714
+ launchImageLibrary({ mediaType: "video" }, async (result) => {
484
715
  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
- });
716
+ const uri = result.assets[0].uri;
717
+ const comp = await compressVideo(uri);
718
+ onComplete({ path: comp, duration: result.assets[0].duration });
491
719
  }
492
720
  });
493
721
  }
494
722
  });
495
723
  };
496
- return (React.createElement(BottomSheet, { visible: visible, onClose: onClose, height: 230 },
724
+ const appliedTextColor = textColor || Colors.white;
725
+ const appliedIconColor = iconTintColor || textColor || Colors.white;
726
+ return (React.createElement(BottomSheet, { visible: visible, onClose: onClose, height: 230, sheetBackground: sheetBackgroundColor },
497
727
  React.createElement(View, { style: styles.container },
498
728
  React.createElement(TouchableOpacity, { style: styles.row, onPress: handleCamera },
499
- React.createElement(Image, { source: Images.video_icon, style: styles.icon }),
500
- React.createElement(Text, { style: styles.text }, "Camera")),
729
+ React.createElement(Image, { source: Images.video_icon, style: [styles.icon, { tintColor: appliedIconColor }] }),
730
+ React.createElement(Text, { style: [styles.text, { color: appliedTextColor }] }, "Camera")),
501
731
  React.createElement(Dividers, { small: true }),
502
732
  React.createElement(TouchableOpacity, { style: styles.row, onPress: handleGallery },
503
- React.createElement(Image, { source: Images.image_icon, style: styles.icon }),
504
- React.createElement(Text, { style: styles.text }, "Gallery")),
733
+ React.createElement(Image, { source: Images.image_icon, style: [styles.icon, { tintColor: appliedIconColor }] }),
734
+ React.createElement(Text, { style: [styles.text, { color: appliedTextColor }] }, "Gallery")),
505
735
  React.createElement(Dividers, { small: true }),
506
736
  React.createElement(TouchableOpacity, { style: styles.row, onPress: onClose },
507
- React.createElement(Image, { source: Images.cancel, style: styles.icon }),
508
- React.createElement(Text, { style: styles.text }, "Cancel")))));
737
+ React.createElement(Image, { source: Images.cancel, style: [styles.icon, { tintColor: appliedIconColor }] }),
738
+ React.createElement(Text, { style: [styles.text, { color: appliedTextColor }] }, "Cancel")))));
509
739
  };
510
740
  export default ImagePicker;
511
741
  const styles = StyleSheet.create({
@@ -513,12 +743,14 @@ const styles = StyleSheet.create({
513
743
  row: {
514
744
  flexDirection: "row",
515
745
  alignItems: "center",
746
+ marginVertical: 6,
747
+ },
748
+ text: {
749
+ ...Typography.style.standardU(),
516
750
  },
517
- text: { ...Typography.style.standardU(), color: Colors.white },
518
751
  icon: {
519
752
  width: Scale.moderateScale(20),
520
753
  height: Scale.moderateScale(20),
521
754
  marginRight: 10,
522
- tintColor: Colors.white,
523
755
  },
524
756
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamnhz/rn-ui-toolkit",
3
- "version": "1.2.8",
3
+ "version": "1.3.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [