@@ -7,51 +7,92 @@ import mimeTypes from 'mime';
7
7
import { MuiFileInput } from 'mui-file-input' ;
8
8
import { memo , useCallback , useEffect , useMemo , useState } from 'react' ;
9
9
10
+ import type React from 'react' ;
11
+
10
12
type ProcessingFormUploadProps = {
11
13
mimes : string [ ] ;
12
14
value : File | null ;
13
15
isOnError : boolean ;
14
16
isPending : boolean ;
15
- onChange : ( value : File | null ) => void ;
17
+ onChange : ( value : File | null , isValid : boolean ) => void ;
16
18
} ;
17
19
18
20
const ProcessingFormUpload = ( { mimes, value, isOnError, isPending, onChange } : ProcessingFormUploadProps ) => {
19
21
const [ file , setFile ] = useState < File | null > ( value ) ;
20
22
const [ isInvalid , setIsInvalid ] = useState ( false ) ;
23
+ const [ isDragging , setIsDragging ] = useState ( false ) ;
24
+ const [ hasAttemptedUpload , setHasAttemptedUpload ] = useState ( false ) ;
21
25
22
26
const stringifiesMineTypes = useMemo ( ( ) => {
23
27
return mimes . join ( ', ' ) ;
24
28
} , [ mimes ] ) ;
25
29
30
+ /**
31
+ * Check file validity and update parent component
32
+ */
26
33
useEffect ( ( ) => {
27
34
let invalid = false ;
28
35
29
- if ( ! file || ! mimes . includes ( mimeTypes . getType ( file . name ) ?? '' ) ) {
30
- invalid = true ;
36
+ if ( file ) {
37
+ if ( ! mimes . includes ( mimeTypes . getType ( file . name ) ?? '' ) ) {
38
+ invalid = true ;
39
+ }
40
+ setHasAttemptedUpload ( true ) ;
31
41
}
32
42
33
43
setIsInvalid ( invalid ) ;
34
- if ( ! invalid ) {
35
- onChange ( file ) ;
36
- }
44
+ onChange ( file , file !== null && ! invalid ) ;
37
45
} , [ file , mimes , onChange ] ) ;
38
46
39
47
const handleFileChange = useCallback ( ( newFile : File | null ) => {
40
48
setFile ( newFile ) ;
49
+ if ( newFile === null ) {
50
+ setHasAttemptedUpload ( false ) ;
51
+ }
52
+ } , [ ] ) ;
53
+
54
+ const handleDragEnter = useCallback ( ( e : React . DragEvent < HTMLDivElement > ) => {
55
+ e . preventDefault ( ) ;
56
+ e . stopPropagation ( ) ;
57
+ setIsDragging ( true ) ;
41
58
} , [ ] ) ;
42
59
60
+ const handleDragLeave = useCallback ( ( e : React . DragEvent < HTMLDivElement > ) => {
61
+ e . preventDefault ( ) ;
62
+ e . stopPropagation ( ) ;
63
+ setIsDragging ( false ) ;
64
+ } , [ ] ) ;
65
+
66
+ const handleDrop = useCallback (
67
+ ( e : React . DragEvent < HTMLDivElement > ) => {
68
+ e . preventDefault ( ) ;
69
+ e . stopPropagation ( ) ;
70
+ setIsDragging ( false ) ;
71
+ if ( e . dataTransfer . files && e . dataTransfer . files . length > 0 ) {
72
+ handleFileChange ( e . dataTransfer . files [ 0 ] ) ;
73
+ }
74
+ } ,
75
+ [ handleFileChange ] ,
76
+ ) ;
77
+
43
78
if ( isPending || isOnError ) {
44
79
return < FileUpload showError = { isOnError } /> ;
45
80
}
46
81
47
82
return (
48
- < div className = "processing-form-field-group processing-form-field-with-label" >
83
+ < div
84
+ className = { `processing-form-field-group processing-form-field-with-label ${ isDragging ? 'dragging' : '' } ` }
85
+ onDragEnter = { handleDragEnter }
86
+ onDragOver = { handleDragEnter }
87
+ onDragLeave = { handleDragLeave }
88
+ onDrop = { handleDrop }
89
+ >
49
90
< MuiFileInput
50
91
className = "processing-form-field"
51
- placeholder = "Déposer un fichier"
92
+ placeholder = "Déposer un fichier ou glisser-déposer ici "
52
93
value = { file }
53
94
onChange = { handleFileChange }
54
- error = { isInvalid }
95
+ error = { isInvalid ? hasAttemptedUpload : false }
55
96
fullWidth
56
97
clearIconButtonProps = { {
57
98
children : < CloseIcon fontSize = "small" /> ,
@@ -68,16 +109,10 @@ const ProcessingFormUpload = ({ mimes, value, isOnError, isPending, onChange }:
68
109
} ,
69
110
} }
70
111
/>
71
- { isInvalid ? (
112
+ { isInvalid && hasAttemptedUpload ? (
72
113
< div className = "text processing-form-field-label error" >
73
- { ! file ? (
74
- < > Fichier manquant</ >
75
- ) : (
76
- < >
77
- Le fichier ne correspond pas à un format compatible, utilisez l'un de ces formats :{ ' ' }
78
- { stringifiesMineTypes } .
79
- </ >
80
- ) }
114
+ Le fichier ne correspond pas à un format compatible, utilisez l'un de ces formats :{ ' ' }
115
+ { stringifiesMineTypes } .
81
116
</ div >
82
117
) : null }
83
118
</ div >
0 commit comments