@technotoil/image-video-editor 0.1.5 → 0.1.6

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,
@@ -508,192 +529,268 @@ export function PickScreen({
508
529
  }, album.id))
509
530
  })]
510
531
  })]
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
532
+ }), /*#__PURE__*/_jsxs(View, {
533
+ style: {
534
+ flex: 1,
535
+ position: 'relative'
525
536
  },
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
537
+ children: [/*#__PURE__*/_jsxs(Animated.View, {
538
+ style: [styles.floatingOverlay, {
539
+ opacity: overlayAnim,
540
+ transform: [{
541
+ translateY: overlayAnim.interpolate({
542
+ inputRange: [0, 1],
543
+ outputRange: [-PREVIEW_HEIGHT, 0]
544
+ })
545
+ }]
546
+ }],
547
+ pointerEvents: isScrolledPast ? 'box-none' : 'none',
548
+ children: [selectedMedia?.type === 'video' ? /*#__PURE__*/_jsxs(Pressable, {
549
+ style: styles.previewImage,
550
+ onPress: () => setVideoPaused(v => !v),
551
+ children: [playableUri && isActive ? /*#__PURE__*/_jsx(VideoPreview, {
552
+ uri: playableUri,
553
+ paused: videoPaused || !isActive,
554
+ muted: false,
555
+ style: styles.previewImage,
556
+ resizeMode: "cover"
557
+ }) : /*#__PURE__*/_jsx(Image, {
558
+ source: {
559
+ uri: selectedMedia.thumbnailUri || selectedMedia.uri
560
+ },
561
+ style: styles.previewImage,
562
+ resizeMode: "cover"
563
+ }), /*#__PURE__*/_jsx(View, {
564
+ style: styles.previewOverlay,
565
+ children: /*#__PURE__*/_jsx(View, {
566
+ style: styles.playPauseCircle,
567
+ children: /*#__PURE__*/_jsx(Ionicons, {
568
+ name: videoPaused ? 'play' : 'pause',
569
+ size: 24,
570
+ color: "#fff"
571
+ })
572
+ })
573
+ })]
574
+ }) : /*#__PURE__*/_jsx(Image, {
575
+ source: {
576
+ uri: previewUri ?? selectedMedia?.uri
577
+ },
578
+ style: [styles.previewImage, cropMode === '1:1' ? styles.squareCrop : styles.originalCrop],
579
+ resizeMode: isRatioLocked ? 'cover' : cropMode === '1:1' ? 'cover' : 'contain'
580
+ }), /*#__PURE__*/_jsxs(View, {
581
+ style: styles.previewControls,
582
+ children: [isRatioLocked && /*#__PURE__*/_jsx(View, {
583
+ style: styles.ratioBadge,
584
+ children: /*#__PURE__*/_jsx(Text, {
585
+ style: styles.ratioBadgeText,
586
+ children: aspectRatio
587
+ })
588
+ }), !isRatioLocked && /*#__PURE__*/_jsx(Pressable, {
589
+ style: styles.cropToggle,
590
+ onPress: () => setCropMode(v => v === '1:1' ? 'original' : '1:1'),
591
+ children: /*#__PURE__*/_jsx(Ionicons, {
592
+ name: cropMode === '1:1' ? 'square-outline' : 'expand-outline',
593
+ size: 20,
594
+ color: "#fff"
595
+ })
596
+ })]
597
+ })]
598
+ }), /*#__PURE__*/_jsx(FlatList, {
599
+ ref: flatListRef,
600
+ data: listData,
601
+ extraData: {
602
+ selectedMedia,
603
+ previewUri,
604
+ playableUri,
605
+ videoPaused,
606
+ cropMode,
607
+ isRatioLocked,
608
+ activeAlbum,
609
+ multiSelect,
610
+ selectedItems,
611
+ showAlbumPicker,
612
+ loading
613
+ },
614
+ keyExtractor: item => item.id,
615
+ stickyHeaderIndices: [1],
616
+ style: styles.libraryList,
617
+ contentContainerStyle: styles.grid,
618
+ onScroll: e => {
619
+ const scrollY = e.nativeEvent.contentOffset.y;
620
+ const scrolledPast = scrollY > PREVIEW_HEIGHT - 10;
621
+ setIsScrolledPast(scrolledPast);
622
+ // When user scrolls back up to see main preview, hide the overlay
623
+ if (!scrolledPast) {
624
+ setShowOverlay(false);
625
+ }
626
+ },
627
+ scrollEventThrottle: 16,
628
+ getItemLayout: (data, index) => {
629
+ const albumRowHeight = 48;
630
+ const rowHeight = Dimensions.get('window').width / 4;
631
+ if (index === 0) return {
632
+ length: PREVIEW_HEIGHT,
633
+ offset: 0,
634
+ index
635
+ };
636
+ if (index === 1) return {
637
+ length: albumRowHeight,
638
+ offset: PREVIEW_HEIGHT,
639
+ index
640
+ };
641
+ const offset = PREVIEW_HEIGHT + albumRowHeight + (index - 2) * rowHeight;
642
+ return {
643
+ length: rowHeight,
644
+ offset,
645
+ index
646
+ };
647
+ },
648
+ removeClippedSubviews: false,
649
+ windowSize: 11,
650
+ ListEmptyComponent: /*#__PURE__*/_jsx(View, {
651
+ style: styles.empty,
652
+ children: /*#__PURE__*/_jsx(Text, {
653
+ style: styles.emptyText,
654
+ children: loading ? 'Loading…' : 'No media found'
655
+ })
656
+ }),
657
+ renderItem: ({
658
+ item
659
+ }) => {
660
+ if (item.type === 'preview') {
661
+ return /*#__PURE__*/_jsxs(Animated.View, {
662
+ style: [styles.preview, {
663
+ transform: [{
664
+ scale: scaleAnim
665
+ }],
666
+ height: PREVIEW_HEIGHT
569
667
  }],
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,
668
+ children: [selectedMedia?.type === 'video' ? /*#__PURE__*/_jsxs(Pressable, {
579
669
  style: styles.previewImage,
580
- resizeMode: "cover"
670
+ onPress: () => setVideoPaused(v => !v),
671
+ children: [playableUri && isActive ? /*#__PURE__*/_jsx(VideoPreview, {
672
+ uri: playableUri,
673
+ paused: videoPaused || !isActive,
674
+ muted: false,
675
+ style: styles.previewImage,
676
+ resizeMode: "cover"
677
+ }) : /*#__PURE__*/_jsx(Image, {
678
+ source: {
679
+ uri: selectedMedia.thumbnailUri || selectedMedia.uri
680
+ },
681
+ style: styles.previewImage,
682
+ resizeMode: "cover"
683
+ }), /*#__PURE__*/_jsx(View, {
684
+ style: styles.previewOverlay,
685
+ children: /*#__PURE__*/_jsx(View, {
686
+ style: styles.playPauseCircle,
687
+ children: /*#__PURE__*/_jsx(Ionicons, {
688
+ name: videoPaused ? 'play' : 'pause',
689
+ size: 24,
690
+ color: "#fff"
691
+ })
692
+ })
693
+ })]
581
694
  }) : /*#__PURE__*/_jsx(Image, {
582
695
  source: {
583
- uri: selectedMedia.thumbnailUri || selectedMedia.uri
696
+ uri: previewUri ?? selectedMedia?.uri
584
697
  },
585
- style: styles.previewImage,
586
- resizeMode: "cover"
587
- }), /*#__PURE__*/_jsx(View, {
588
- style: styles.previewOverlay,
589
- children: /*#__PURE__*/_jsx(View, {
590
- style: styles.playPauseCircle,
698
+ style: [styles.previewImage, cropMode === '1:1' ? styles.squareCrop : styles.originalCrop],
699
+ resizeMode: isRatioLocked ? 'cover' : cropMode === '1:1' ? 'cover' : 'contain'
700
+ }), /*#__PURE__*/_jsxs(View, {
701
+ style: styles.previewControls,
702
+ children: [isRatioLocked && /*#__PURE__*/_jsx(View, {
703
+ style: styles.ratioBadge,
704
+ children: /*#__PURE__*/_jsx(Text, {
705
+ style: styles.ratioBadgeText,
706
+ children: aspectRatio
707
+ })
708
+ }), !isRatioLocked && /*#__PURE__*/_jsx(Pressable, {
709
+ style: styles.cropToggle,
710
+ onPress: () => setCropMode(v => v === '1:1' ? 'original' : '1:1'),
591
711
  children: /*#__PURE__*/_jsx(Ionicons, {
592
- name: videoPaused ? 'play' : 'pause',
593
- size: 24,
712
+ name: cropMode === '1:1' ? 'square-outline' : 'expand-outline',
713
+ size: 20,
594
714
  color: "#fff"
595
715
  })
596
- })
716
+ })]
597
717
  })]
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();
718
+ });
719
+ }
720
+ if (item.type === 'albumRow') {
721
+ return /*#__PURE__*/_jsxs(View, {
722
+ style: [styles.albumRow, {
723
+ backgroundColor: '#0b0f1a',
724
+ zIndex: 999,
725
+ elevation: 5
726
+ }],
727
+ children: [/*#__PURE__*/_jsxs(Pressable, {
728
+ style: styles.albumSelector,
729
+ onPress: () => setShowAlbumPicker(v => !v),
730
+ children: [/*#__PURE__*/_jsx(Text, {
731
+ style: styles.albumTitle,
732
+ children: activeAlbum.title
733
+ }), /*#__PURE__*/_jsx(Ionicons, {
734
+ name: "chevron-down",
735
+ size: 16,
736
+ color: "#fff",
737
+ style: {
738
+ marginLeft: 6
667
739
  }
740
+ })]
741
+ }), maxSelection > 1 && /*#__PURE__*/_jsxs(View, {
742
+ style: {
743
+ flexDirection: 'row',
744
+ alignItems: 'center',
745
+ gap: 8
668
746
  },
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
- })
747
+ children: [multiSelect && /*#__PURE__*/_jsxs(Text, {
748
+ style: {
749
+ color: '#3b82f6',
750
+ fontSize: 12,
751
+ fontWeight: '700'
752
+ },
753
+ children: [selectedItems.length, "/", maxSelection]
754
+ }), /*#__PURE__*/_jsx(Pressable, {
755
+ style: [styles.multiSelectBtn, multiSelect && styles.multiSelectBtnActive, multiSelect && selectedItems.length === 0 && {
756
+ opacity: 0.35
757
+ }],
758
+ onPress: () => {
759
+ if (multiSelect) {
760
+ handleNext();
761
+ } else {
762
+ toggleMultiSelect();
763
+ }
764
+ },
765
+ disabled: multiSelect && selectedItems.length === 0,
766
+ children: /*#__PURE__*/_jsx(Text, {
767
+ style: [styles.multiSelectText, multiSelect && styles.multiSelectTextActive],
768
+ children: multiSelect ? 'Done' : 'Select multiple'
769
+ })
770
+ })]
674
771
  })]
675
- })]
772
+ });
773
+ }
774
+ return /*#__PURE__*/_jsx(View, {
775
+ style: {
776
+ flexDirection: 'row'
777
+ },
778
+ children: item.items.map(mediaItem => renderThumb({
779
+ item: mediaItem
780
+ }))
676
781
  });
677
782
  }
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, {
783
+ }), /*#__PURE__*/_jsx(View, {
784
+ style: styles.tabBar,
785
+ children: mediaTabs.filter(t => mediaType === 'any' || mediaType === 'photo' && t !== 'VIDEO' || mediaType === 'video' && t !== 'PHOTO').map(t => /*#__PURE__*/_jsx(Pressable, {
786
+ onPress: () => setTab(t),
787
+ children: /*#__PURE__*/_jsx(Text, {
788
+ style: [styles.tab, tab === t && styles.tabActive],
789
+ children: t
790
+ })
791
+ }, t))
792
+ })]
793
+ }), " ", /*#__PURE__*/_jsx(Modal, {
697
794
  visible: showCustomCamera,
698
795
  animationType: "slide",
699
796
  transparent: false,
@@ -931,10 +1028,21 @@ const styles = StyleSheet.create({
931
1028
  },
932
1029
  preview: {
933
1030
  width: '100%',
934
- height: 420,
935
1031
  backgroundColor: '#0f172a',
936
1032
  overflow: 'hidden'
937
1033
  },
1034
+ floatingOverlay: {
1035
+ position: 'absolute',
1036
+ top: 0,
1037
+ left: 0,
1038
+ right: 0,
1039
+ zIndex: 100,
1040
+ elevation: 20,
1041
+ backgroundColor: '#0f172a',
1042
+ overflow: 'hidden',
1043
+ width: '100%',
1044
+ height: Dimensions.get('window').width
1045
+ },
938
1046
  previewImage: {
939
1047
  width: '100%',
940
1048
  height: '100%',