1
1
import { Input } from "@/components/ui/input" ;
2
2
import { Button } from "@/components/ui/button" ;
3
3
import { X } from "lucide-react" ;
4
- import { FC , useState , useRef } from "react" ;
4
+ import { FC , useState , useRef , useCallback } from "react" ;
5
+ import ImageModal from "./ImageModal" ;
6
+ import { getCroppedImg } from "./cropImage" ;
7
+ import { Area } from "react-easy-crop" ;
5
8
6
9
const PictureInput : FC = ( ) => {
7
- const [ image , setImage ] = useState < string | null > (
8
- "https://res.cloudinary.com/dwyxogyrk/image/upload/v1737433466/h0xf7zi0blmclfqrjeo7.png"
9
- ) ;
10
+ const [ image , setImage ] = useState < string | null > ( null ) ;
11
+ const [ croppedImage , setCroppedImage ] = useState < string | null > ( null ) ;
12
+ const [ imageModal , setImageModal ] = useState < boolean > ( false ) ;
10
13
const fileInputRef = useRef < HTMLInputElement > ( null ) ;
14
+ const [ crop , setCrop ] = useState ( { x : 0 , y : 0 } ) ;
15
+ const [ zoom , setZoom ] = useState ( 1 ) ;
16
+ const [ croppedAreaPixels , setCroppedAreaPixels ] = useState < Area | null > ( null ) ;
11
17
12
18
// Handle file selection
13
19
const handleFileChange = ( event : React . ChangeEvent < HTMLInputElement > ) => {
@@ -16,8 +22,8 @@ const PictureInput: FC = () => {
16
22
const reader = new FileReader ( ) ;
17
23
reader . onload = ( ) => setImage ( reader . result as string ) ;
18
24
reader . readAsDataURL ( file ) ;
25
+ setImageModal ( true ) ;
19
26
}
20
-
21
27
// Reset input value to allow selecting the same file again
22
28
if ( fileInputRef . current ) {
23
29
fileInputRef . current . value = "" ;
@@ -30,7 +36,27 @@ const PictureInput: FC = () => {
30
36
} ;
31
37
32
38
// Remove selected image
33
- const removeImage = ( ) => setImage ( null ) ;
39
+ const removeImage = ( ) => {
40
+ setImage ( null ) ;
41
+ setCroppedImage ( null ) ;
42
+ }
43
+
44
+ const onCropComplete = useCallback ( ( _val : Area ,
45
+ croppedAreaPixels : Area ) => {
46
+ setCroppedAreaPixels ( croppedAreaPixels ) ;
47
+ } , [ ] ) ;
48
+
49
+ const handleSaveImage = async ( ) => {
50
+ if ( image && croppedAreaPixels ) {
51
+ try {
52
+ const croppedImage = await getCroppedImg ( image , croppedAreaPixels ) ;
53
+ setCroppedImage ( croppedImage ) ;
54
+ setImageModal ( false ) ;
55
+ } catch ( e ) {
56
+ console . error ( e ) ;
57
+ }
58
+ }
59
+ }
34
60
35
61
return (
36
62
< div className = "flex w-full max-w-sm items-center gap-3 bg-gray-200 dark:bg-gray-900 p-4 rounded-2xl relative shadow-md" >
@@ -43,8 +69,9 @@ const PictureInput: FC = () => {
43
69
</ button >
44
70
) }
45
71
46
- { image ? (
47
- < img className = "w-24 h-24 rounded-2xl object-cover border border-gray-300 dark:border-gray-700" src = { image } alt = "Profile" />
72
+ { croppedImage ? (
73
+ < img className = "w-24 h-24 rounded-2xl object-cover border border-gray-300
74
+ dark:border-gray-700" src = { croppedImage } alt = "Profile" />
48
75
) : (
49
76
< div className = "w-24 h-24 rounded-2xl bg-gray-400 dark:bg-gray-800 flex items-center justify-center text-white" >
50
77
No Image
@@ -65,7 +92,7 @@ const PictureInput: FC = () => {
65
92
{ /* Button to Trigger File Input */ }
66
93
< button
67
94
onClick = { triggerFileInput }
68
- className = "text-sm"
95
+ className = "text-sm cursor-pointer "
69
96
>
70
97
Upload Image
71
98
</ button >
@@ -78,8 +105,20 @@ const PictureInput: FC = () => {
78
105
</ Button >
79
106
) }
80
107
</ div >
108
+ { imageModal && (
109
+ < ImageModal
110
+ image = { image }
111
+ crop = { crop }
112
+ setCrop = { setCrop }
113
+ zoom = { zoom }
114
+ setZoom = { setZoom }
115
+ onCropComplete = { onCropComplete }
116
+ handleSave = { handleSaveImage }
117
+ closeModal = { ( ) => setImageModal ( false ) }
118
+ />
119
+ ) }
81
120
</ div >
82
121
) ;
83
122
} ;
84
123
85
- export default PictureInput ;
124
+ export default PictureInput ;
0 commit comments