@technotoil/image-video-editor 0.1.5 → 0.1.7

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.
@@ -80,6 +80,12 @@ export function PickScreen({
80
80
  const [albums, setAlbums] = useState([]);
81
81
  const [loading, setLoading] = useState(false);
82
82
  const [previewUri, setPreviewUri] = useState(null);
83
+ // Track if user has scrolled past the main preview
84
+ const [isScrolledPast, setIsScrolledPast] = useState(false);
85
+ // Only show overlay when user explicitly clicks media while scrolled past preview
86
+ const [showOverlay, setShowOverlay] = useState(false);
87
+ const flatListRef = useRef(null);
88
+ const PREVIEW_HEIGHT = Dimensions.get('window').width;
83
89
 
84
90
  // Aspect ratio logic
85
91
  const isRatioLocked = aspectRatio !== 'free';
@@ -95,6 +101,7 @@ export function PickScreen({
95
101
  const [cropMode, setCropMode] = useState(isRatioLocked ? '1:1' : '1:1');
96
102
  const [videoPaused, setVideoPaused] = useState(false);
97
103
  const scaleAnim = useRef(new Animated.Value(1)).current;
104
+ const overlayAnim = useRef(new Animated.Value(0)).current;
98
105
  const [showCustomCamera, setShowCustomCamera] = useState(false);
99
106
  const [facing, setFacing] = useState('front');
100
107
  const [flashMode, setFlashMode] = useState('off');
@@ -237,6 +244,15 @@ export function PickScreen({
237
244
  type: 'albumRow'
238
245
  }, ...rows];
239
246
  }, [filtered]);
247
+
248
+ // Show/hide floating overlay with animation
249
+ useEffect(() => {
250
+ Animated.timing(overlayAnim, {
251
+ toValue: showOverlay ? 1 : 0,
252
+ duration: 200,
253
+ useNativeDriver: true
254
+ }).start();
255
+ }, [showOverlay]);
240
256
  useEffect(() => {
241
257
  let cancelled = false;
242
258
  setVideoPaused(false);
@@ -292,6 +308,11 @@ export function PickScreen({
292
308
  } else {
293
309
  setSelectedMedia(item);
294
310
  }
311
+
312
+ // Show floating overlay only if user is already scrolled past the main preview
313
+ if (isScrolledPast) {
314
+ setShowOverlay(true);
315
+ }
295
316
  Animated.sequence([Animated.timing(scaleAnim, {
296
317
  toValue: 0.97,
297
318
  duration: 80,
@@ -425,7 +446,11 @@ export function PickScreen({
425
446
  children: selIdx
426
447
  })
427
448
  }) : /*#__PURE__*/_jsx(View, {
428
- style: styles.emptyBadge
449
+ style: [styles.emptyBadge, selectedItems.length >= maxSelection ? {
450
+ opacity: 0.3,
451
+ backgroundColor: 'rgba(0,0,0,0.3)',
452
+ borderColor: 'rgba(255,255,255,0.5)'
453
+ } : null]
429
454
  })
430
455
  }), item.type === 'video' && /*#__PURE__*/_jsxs(_Fragment, {
431
456
  children: [/*#__PURE__*/_jsx(View, {
@@ -508,192 +533,268 @@ export function PickScreen({
508
533
  }, album.id))
509
534
  })]
510
535
  })]
511
- }), /*#__PURE__*/_jsx(FlatList, {
512
- data: listData,
513
- extraData: {
514
- selectedMedia,
515
- previewUri,
516
- playableUri,
517
- videoPaused,
518
- cropMode,
519
- isRatioLocked,
520
- activeAlbum,
521
- multiSelect,
522
- selectedItems,
523
- showAlbumPicker,
524
- loading
536
+ }), /*#__PURE__*/_jsxs(View, {
537
+ style: {
538
+ flex: 1,
539
+ position: 'relative'
525
540
  },
526
- keyExtractor: item => item.id,
527
- stickyHeaderIndices: [1],
528
- style: styles.libraryList,
529
- contentContainerStyle: styles.grid,
530
- getItemLayout: (data, index) => {
531
- const previewHeight = isRatioLocked && previewAspectRatio ? Dimensions.get('window').width / previewAspectRatio : 420;
532
- const albumRowHeight = 48;
533
- const rowHeight = Dimensions.get('window').width / 4;
534
- if (index === 0) return {
535
- length: previewHeight,
536
- offset: 0,
537
- index
538
- };
539
- if (index === 1) return {
540
- length: albumRowHeight,
541
- offset: previewHeight,
542
- index
543
- };
544
- const offset = previewHeight + albumRowHeight + (index - 2) * rowHeight;
545
- return {
546
- length: rowHeight,
547
- offset,
548
- index
549
- };
550
- },
551
- removeClippedSubviews: false,
552
- windowSize: 11,
553
- ListEmptyComponent: /*#__PURE__*/_jsx(View, {
554
- style: styles.empty,
555
- children: /*#__PURE__*/_jsx(Text, {
556
- style: styles.emptyText,
557
- children: loading ? 'Loading…' : 'No media found'
558
- })
559
- }),
560
- renderItem: ({
561
- item
562
- }) => {
563
- if (item.type === 'preview') {
564
- const previewStyleHeight = isRatioLocked && previewAspectRatio ? Dimensions.get('window').width / previewAspectRatio : 420;
565
- return /*#__PURE__*/_jsxs(Animated.View, {
566
- style: [styles.preview, {
567
- transform: [{
568
- scale: scaleAnim
541
+ children: [/*#__PURE__*/_jsxs(Animated.View, {
542
+ style: [styles.floatingOverlay, {
543
+ opacity: overlayAnim,
544
+ transform: [{
545
+ translateY: overlayAnim.interpolate({
546
+ inputRange: [0, 1],
547
+ outputRange: [-PREVIEW_HEIGHT, 0]
548
+ })
549
+ }]
550
+ }],
551
+ pointerEvents: isScrolledPast ? 'box-none' : 'none',
552
+ children: [selectedMedia?.type === 'video' ? /*#__PURE__*/_jsxs(Pressable, {
553
+ style: styles.previewImage,
554
+ onPress: () => setVideoPaused(v => !v),
555
+ children: [playableUri && isActive ? /*#__PURE__*/_jsx(VideoPreview, {
556
+ uri: playableUri,
557
+ paused: videoPaused || !isActive,
558
+ muted: false,
559
+ style: styles.previewImage,
560
+ resizeMode: "cover"
561
+ }) : /*#__PURE__*/_jsx(Image, {
562
+ source: {
563
+ uri: selectedMedia.thumbnailUri || selectedMedia.uri
564
+ },
565
+ style: styles.previewImage,
566
+ resizeMode: "cover"
567
+ }), /*#__PURE__*/_jsx(View, {
568
+ style: styles.previewOverlay,
569
+ children: /*#__PURE__*/_jsx(View, {
570
+ style: styles.playPauseCircle,
571
+ children: /*#__PURE__*/_jsx(Ionicons, {
572
+ name: videoPaused ? 'play' : 'pause',
573
+ size: 24,
574
+ color: "#fff"
575
+ })
576
+ })
577
+ })]
578
+ }) : /*#__PURE__*/_jsx(Image, {
579
+ source: {
580
+ uri: previewUri ?? selectedMedia?.uri
581
+ },
582
+ style: [styles.previewImage, cropMode === '1:1' ? styles.squareCrop : styles.originalCrop],
583
+ resizeMode: isRatioLocked ? 'cover' : cropMode === '1:1' ? 'cover' : 'contain'
584
+ }), /*#__PURE__*/_jsxs(View, {
585
+ style: styles.previewControls,
586
+ children: [isRatioLocked && /*#__PURE__*/_jsx(View, {
587
+ style: styles.ratioBadge,
588
+ children: /*#__PURE__*/_jsx(Text, {
589
+ style: styles.ratioBadgeText,
590
+ children: aspectRatio
591
+ })
592
+ }), !isRatioLocked && /*#__PURE__*/_jsx(Pressable, {
593
+ style: styles.cropToggle,
594
+ onPress: () => setCropMode(v => v === '1:1' ? 'original' : '1:1'),
595
+ children: /*#__PURE__*/_jsx(Ionicons, {
596
+ name: cropMode === '1:1' ? 'square-outline' : 'expand-outline',
597
+ size: 20,
598
+ color: "#fff"
599
+ })
600
+ })]
601
+ })]
602
+ }), /*#__PURE__*/_jsx(FlatList, {
603
+ ref: flatListRef,
604
+ data: listData,
605
+ extraData: {
606
+ selectedMedia,
607
+ previewUri,
608
+ playableUri,
609
+ videoPaused,
610
+ cropMode,
611
+ isRatioLocked,
612
+ activeAlbum,
613
+ multiSelect,
614
+ selectedItems,
615
+ showAlbumPicker,
616
+ loading
617
+ },
618
+ keyExtractor: item => item.id,
619
+ stickyHeaderIndices: [1],
620
+ style: styles.libraryList,
621
+ contentContainerStyle: styles.grid,
622
+ onScroll: e => {
623
+ const scrollY = e.nativeEvent.contentOffset.y;
624
+ const scrolledPast = scrollY > PREVIEW_HEIGHT - 10;
625
+ setIsScrolledPast(scrolledPast);
626
+ // When user scrolls back up to see main preview, hide the overlay
627
+ if (!scrolledPast) {
628
+ setShowOverlay(false);
629
+ }
630
+ },
631
+ scrollEventThrottle: 16,
632
+ getItemLayout: (data, index) => {
633
+ const albumRowHeight = 48;
634
+ const rowHeight = Dimensions.get('window').width / 4;
635
+ if (index === 0) return {
636
+ length: PREVIEW_HEIGHT,
637
+ offset: 0,
638
+ index
639
+ };
640
+ if (index === 1) return {
641
+ length: albumRowHeight,
642
+ offset: PREVIEW_HEIGHT,
643
+ index
644
+ };
645
+ const offset = PREVIEW_HEIGHT + albumRowHeight + (index - 2) * rowHeight;
646
+ return {
647
+ length: rowHeight,
648
+ offset,
649
+ index
650
+ };
651
+ },
652
+ removeClippedSubviews: false,
653
+ windowSize: 11,
654
+ ListEmptyComponent: /*#__PURE__*/_jsx(View, {
655
+ style: styles.empty,
656
+ children: /*#__PURE__*/_jsx(Text, {
657
+ style: styles.emptyText,
658
+ children: loading ? 'Loading…' : 'No media found'
659
+ })
660
+ }),
661
+ renderItem: ({
662
+ item
663
+ }) => {
664
+ if (item.type === 'preview') {
665
+ return /*#__PURE__*/_jsxs(Animated.View, {
666
+ style: [styles.preview, {
667
+ transform: [{
668
+ scale: scaleAnim
669
+ }],
670
+ height: PREVIEW_HEIGHT
569
671
  }],
570
- height: previewStyleHeight
571
- }],
572
- children: [selectedMedia?.type === 'video' ? /*#__PURE__*/_jsxs(Pressable, {
573
- style: styles.previewImage,
574
- onPress: () => setVideoPaused(v => !v),
575
- children: [playableUri && isActive ? /*#__PURE__*/_jsx(VideoPreview, {
576
- uri: playableUri,
577
- paused: videoPaused || !isActive,
578
- muted: false,
672
+ children: [selectedMedia?.type === 'video' ? /*#__PURE__*/_jsxs(Pressable, {
579
673
  style: styles.previewImage,
580
- resizeMode: "cover"
674
+ onPress: () => setVideoPaused(v => !v),
675
+ children: [playableUri && isActive ? /*#__PURE__*/_jsx(VideoPreview, {
676
+ uri: playableUri,
677
+ paused: videoPaused || !isActive,
678
+ muted: false,
679
+ style: styles.previewImage,
680
+ resizeMode: "cover"
681
+ }) : /*#__PURE__*/_jsx(Image, {
682
+ source: {
683
+ uri: selectedMedia.thumbnailUri || selectedMedia.uri
684
+ },
685
+ style: styles.previewImage,
686
+ resizeMode: "cover"
687
+ }), /*#__PURE__*/_jsx(View, {
688
+ style: styles.previewOverlay,
689
+ children: /*#__PURE__*/_jsx(View, {
690
+ style: styles.playPauseCircle,
691
+ children: /*#__PURE__*/_jsx(Ionicons, {
692
+ name: videoPaused ? 'play' : 'pause',
693
+ size: 24,
694
+ color: "#fff"
695
+ })
696
+ })
697
+ })]
581
698
  }) : /*#__PURE__*/_jsx(Image, {
582
699
  source: {
583
- uri: selectedMedia.thumbnailUri || selectedMedia.uri
700
+ uri: previewUri ?? selectedMedia?.uri
584
701
  },
585
- style: styles.previewImage,
586
- resizeMode: "cover"
587
- }), /*#__PURE__*/_jsx(View, {
588
- style: styles.previewOverlay,
589
- children: /*#__PURE__*/_jsx(View, {
590
- style: styles.playPauseCircle,
702
+ style: [styles.previewImage, cropMode === '1:1' ? styles.squareCrop : styles.originalCrop],
703
+ resizeMode: isRatioLocked ? 'cover' : cropMode === '1:1' ? 'cover' : 'contain'
704
+ }), /*#__PURE__*/_jsxs(View, {
705
+ style: styles.previewControls,
706
+ children: [isRatioLocked && /*#__PURE__*/_jsx(View, {
707
+ style: styles.ratioBadge,
708
+ children: /*#__PURE__*/_jsx(Text, {
709
+ style: styles.ratioBadgeText,
710
+ children: aspectRatio
711
+ })
712
+ }), !isRatioLocked && /*#__PURE__*/_jsx(Pressable, {
713
+ style: styles.cropToggle,
714
+ onPress: () => setCropMode(v => v === '1:1' ? 'original' : '1:1'),
591
715
  children: /*#__PURE__*/_jsx(Ionicons, {
592
- name: videoPaused ? 'play' : 'pause',
593
- size: 24,
716
+ name: cropMode === '1:1' ? 'square-outline' : 'expand-outline',
717
+ size: 20,
594
718
  color: "#fff"
595
719
  })
596
- })
720
+ })]
597
721
  })]
598
- }) : /*#__PURE__*/_jsx(Image, {
599
- source: {
600
- uri: previewUri ?? selectedMedia?.uri
601
- },
602
- style: [styles.previewImage, cropMode === '1:1' ? styles.squareCrop : styles.originalCrop],
603
- resizeMode: isRatioLocked ? 'cover' : cropMode === '1:1' ? 'cover' : 'contain'
604
- }), /*#__PURE__*/_jsxs(View, {
605
- style: styles.previewControls,
606
- children: [isRatioLocked && /*#__PURE__*/_jsx(View, {
607
- style: styles.ratioBadge,
608
- children: /*#__PURE__*/_jsx(Text, {
609
- style: styles.ratioBadgeText,
610
- children: aspectRatio
611
- })
612
- }), !isRatioLocked && /*#__PURE__*/_jsx(Pressable, {
613
- style: styles.cropToggle,
614
- onPress: () => setCropMode(v => v === '1:1' ? 'original' : '1:1'),
615
- children: /*#__PURE__*/_jsx(Ionicons, {
616
- name: cropMode === '1:1' ? 'square-outline' : 'expand-outline',
617
- size: 20,
618
- color: "#fff"
619
- })
620
- })]
621
- })]
622
- });
623
- }
624
- if (item.type === 'albumRow') {
625
- return /*#__PURE__*/_jsxs(View, {
626
- style: [styles.albumRow, {
627
- backgroundColor: '#0b0f1a',
628
- zIndex: 999,
629
- elevation: 5
630
- }],
631
- children: [/*#__PURE__*/_jsxs(Pressable, {
632
- style: styles.albumSelector,
633
- onPress: () => setShowAlbumPicker(v => !v),
634
- children: [/*#__PURE__*/_jsx(Text, {
635
- style: styles.albumTitle,
636
- children: activeAlbum.title
637
- }), /*#__PURE__*/_jsx(Ionicons, {
638
- name: "chevron-down",
639
- size: 16,
640
- color: "#fff",
641
- style: {
642
- marginLeft: 6
643
- }
644
- })]
645
- }), maxSelection > 1 && /*#__PURE__*/_jsxs(View, {
646
- style: {
647
- flexDirection: 'row',
648
- alignItems: 'center',
649
- gap: 8
650
- },
651
- children: [multiSelect && /*#__PURE__*/_jsxs(Text, {
652
- style: {
653
- color: '#3b82f6',
654
- fontSize: 12,
655
- fontWeight: '700'
656
- },
657
- children: [selectedItems.length, "/", maxSelection]
658
- }), /*#__PURE__*/_jsx(Pressable, {
659
- style: [styles.multiSelectBtn, multiSelect && styles.multiSelectBtnActive, multiSelect && selectedItems.length === 0 && {
660
- opacity: 0.35
661
- }],
662
- onPress: () => {
663
- if (multiSelect) {
664
- handleNext();
665
- } else {
666
- toggleMultiSelect();
722
+ });
723
+ }
724
+ if (item.type === 'albumRow') {
725
+ return /*#__PURE__*/_jsxs(View, {
726
+ style: [styles.albumRow, {
727
+ backgroundColor: '#0b0f1a',
728
+ zIndex: 999,
729
+ elevation: 5
730
+ }],
731
+ children: [/*#__PURE__*/_jsxs(Pressable, {
732
+ style: styles.albumSelector,
733
+ onPress: () => setShowAlbumPicker(v => !v),
734
+ children: [/*#__PURE__*/_jsx(Text, {
735
+ style: styles.albumTitle,
736
+ children: activeAlbum.title
737
+ }), /*#__PURE__*/_jsx(Ionicons, {
738
+ name: "chevron-down",
739
+ size: 16,
740
+ color: "#fff",
741
+ style: {
742
+ marginLeft: 6
667
743
  }
744
+ })]
745
+ }), maxSelection > 1 && /*#__PURE__*/_jsxs(View, {
746
+ style: {
747
+ flexDirection: 'row',
748
+ alignItems: 'center',
749
+ gap: 8
668
750
  },
669
- disabled: multiSelect && selectedItems.length === 0,
670
- children: /*#__PURE__*/_jsx(Text, {
671
- style: [styles.multiSelectText, multiSelect && styles.multiSelectTextActive],
672
- children: multiSelect ? 'Done' : 'Select multiple'
673
- })
751
+ children: [multiSelect && /*#__PURE__*/_jsxs(Text, {
752
+ style: {
753
+ color: '#3b82f6',
754
+ fontSize: 12,
755
+ fontWeight: '700'
756
+ },
757
+ children: [selectedItems.length, "/", maxSelection]
758
+ }), /*#__PURE__*/_jsx(Pressable, {
759
+ style: [styles.multiSelectBtn, multiSelect && styles.multiSelectBtnActive, multiSelect && selectedItems.length === 0 && {
760
+ opacity: 0.35
761
+ }],
762
+ onPress: () => {
763
+ if (multiSelect) {
764
+ handleNext();
765
+ } else {
766
+ toggleMultiSelect();
767
+ }
768
+ },
769
+ disabled: multiSelect && selectedItems.length === 0,
770
+ children: /*#__PURE__*/_jsx(Text, {
771
+ style: [styles.multiSelectText, multiSelect && styles.multiSelectTextActive],
772
+ children: multiSelect ? 'Done' : 'Select multiple'
773
+ })
774
+ })]
674
775
  })]
675
- })]
776
+ });
777
+ }
778
+ return /*#__PURE__*/_jsx(View, {
779
+ style: {
780
+ flexDirection: 'row'
781
+ },
782
+ children: item.items.map(mediaItem => renderThumb({
783
+ item: mediaItem
784
+ }))
676
785
  });
677
786
  }
678
- return /*#__PURE__*/_jsx(View, {
679
- style: {
680
- flexDirection: 'row'
681
- },
682
- children: item.items.map(mediaItem => renderThumb({
683
- item: mediaItem
684
- }))
685
- });
686
- }
687
- }), /*#__PURE__*/_jsx(View, {
688
- style: styles.tabBar,
689
- children: mediaTabs.filter(t => mediaType === 'any' || mediaType === 'photo' && t !== 'VIDEO' || mediaType === 'video' && t !== 'PHOTO').map(t => /*#__PURE__*/_jsx(Pressable, {
690
- onPress: () => setTab(t),
691
- children: /*#__PURE__*/_jsx(Text, {
692
- style: [styles.tab, tab === t && styles.tabActive],
693
- children: t
694
- })
695
- }, t))
696
- }), /*#__PURE__*/_jsx(Modal, {
787
+ }), /*#__PURE__*/_jsx(View, {
788
+ style: styles.tabBar,
789
+ children: mediaTabs.filter(t => mediaType === 'any' || mediaType === 'photo' && t !== 'VIDEO' || mediaType === 'video' && t !== 'PHOTO').map(t => /*#__PURE__*/_jsx(Pressable, {
790
+ onPress: () => setTab(t),
791
+ children: /*#__PURE__*/_jsx(Text, {
792
+ style: [styles.tab, tab === t && styles.tabActive],
793
+ children: t
794
+ })
795
+ }, t))
796
+ })]
797
+ }), " ", /*#__PURE__*/_jsx(Modal, {
697
798
  visible: showCustomCamera,
698
799
  animationType: "slide",
699
800
  transparent: false,
@@ -931,10 +1032,21 @@ const styles = StyleSheet.create({
931
1032
  },
932
1033
  preview: {
933
1034
  width: '100%',
934
- height: 420,
935
1035
  backgroundColor: '#0f172a',
936
1036
  overflow: 'hidden'
937
1037
  },
1038
+ floatingOverlay: {
1039
+ position: 'absolute',
1040
+ top: 0,
1041
+ left: 0,
1042
+ right: 0,
1043
+ zIndex: 100,
1044
+ elevation: 20,
1045
+ backgroundColor: '#0f172a',
1046
+ overflow: 'hidden',
1047
+ width: '100%',
1048
+ height: Dimensions.get('window').width
1049
+ },
938
1050
  previewImage: {
939
1051
  width: '100%',
940
1052
  height: '100%',