1
1
import PropTypes from 'prop-types' ;
2
2
import React , {
3
3
useContext ,
4
+ useEffect ,
4
5
useState ,
5
6
} from 'react' ;
6
7
import { withGlobalProps } from '../../providers/globalProps' ;
@@ -24,6 +25,11 @@ export const FileInputField = React.forwardRef((props, ref) => {
24
25
isLabelVisible,
25
26
label,
26
27
layout,
28
+ onChange,
29
+ onDragEnter,
30
+ onDragLeave,
31
+ onDragOver,
32
+ onDrop,
27
33
required,
28
34
size,
29
35
validationState,
@@ -36,10 +42,10 @@ export const FileInputField = React.forwardRef((props, ref) => {
36
42
const translations = useContext ( TranslationsContext ) ;
37
43
38
44
const [ selectedFileNames , setSelectedFileNames ] = useState ( [ ] ) ;
45
+ const [ isDragAndDropSupported , setIsDragAndDropSupported ] = useState ( false ) ;
46
+ const [ isDragging , setIsDragging ] = useState ( false ) ;
39
47
40
- const handleFileChange = ( event ) => {
41
- const { files } = event . target ;
42
-
48
+ const handleFileChange = ( files ) => {
43
49
if ( files . length === 0 ) {
44
50
setSelectedFileNames ( [ ] ) ;
45
51
return ;
@@ -54,6 +60,53 @@ export const FileInputField = React.forwardRef((props, ref) => {
54
60
setSelectedFileNames ( fileNames ) ;
55
61
} ;
56
62
63
+ const handleInputChange = ( event ) => {
64
+ handleFileChange ( event . target . files ) ;
65
+
66
+ if ( props ?. onChange ) {
67
+ props . onChange ( event ) ;
68
+ }
69
+ } ;
70
+
71
+ const handleDrop = ( event ) => {
72
+ event . preventDefault ( ) ;
73
+ handleFileChange ( event . dataTransfer . files ) ;
74
+ setIsDragging ( false ) ;
75
+
76
+ if ( props ?. onDrop ) {
77
+ props . onDrop ( event ) ;
78
+ }
79
+ } ;
80
+
81
+ const handleDragOver = ( event ) => {
82
+ event . preventDefault ( ) ;
83
+ setIsDragging ( true ) ;
84
+
85
+ if ( props ?. onDragOver ) {
86
+ props . onDragOver ( event ) ;
87
+ }
88
+ } ;
89
+
90
+ const handleDragEnter = ( event ) => {
91
+ setIsDragging ( true ) ;
92
+
93
+ if ( props ?. onDragEnter ) {
94
+ props . onDragEnter ( event ) ;
95
+ }
96
+ } ;
97
+
98
+ const handleDragLeave = ( event ) => {
99
+ setIsDragging ( false ) ;
100
+
101
+ if ( props ?. onDragLeave ) {
102
+ props . onDragLeave ( event ) ;
103
+ }
104
+ } ;
105
+
106
+ useEffect ( ( ) => {
107
+ setIsDragAndDropSupported ( 'draggable' in document . createElement ( 'span' ) ) ;
108
+ } , [ ] ) ;
109
+
57
110
return (
58
111
< label
59
112
className = { classNames (
@@ -65,6 +118,7 @@ export const FileInputField = React.forwardRef((props, ref) => {
65
118
: styles . isRootLayoutVertical ,
66
119
resolveContextOrProp ( inputGroupContext && inputGroupContext . disabled , disabled ) && styles . isRootDisabled ,
67
120
inputGroupContext && styles . isRootGrouped ,
121
+ isDragging && styles . isRootDragging ,
68
122
required && styles . isRootRequired ,
69
123
getRootSizeClassName (
70
124
resolveContextOrProp ( inputGroupContext && inputGroupContext . size , size ) ,
@@ -74,6 +128,10 @@ export const FileInputField = React.forwardRef((props, ref) => {
74
128
) }
75
129
htmlFor = { id }
76
130
id = { id && `${ id } __label` }
131
+ onDragEnter = { ! disabled && isDragAndDropSupported ? handleDragEnter : undefined }
132
+ onDragLeave = { ! disabled && isDragAndDropSupported ? handleDragLeave : undefined }
133
+ onDragOver = { ! disabled && isDragAndDropSupported ? handleDragOver : undefined }
134
+ onDrop = { ! disabled && isDragAndDropSupported ? handleDrop : undefined }
77
135
>
78
136
< div
79
137
className = { classNames (
@@ -91,7 +149,7 @@ export const FileInputField = React.forwardRef((props, ref) => {
91
149
className = { styles . input }
92
150
disabled = { resolveContextOrProp ( inputGroupContext && inputGroupContext . disabled , disabled ) }
93
151
id = { id }
94
- onChange = { handleFileChange }
152
+ onChange = { handleInputChange }
95
153
ref = { ref }
96
154
required = { required }
97
155
type = "file"
@@ -100,9 +158,8 @@ export const FileInputField = React.forwardRef((props, ref) => {
100
158
< Text lines = { 1 } >
101
159
{ ! selectedFileNames . length && (
102
160
< >
103
- { translations . FileInputField . drop }
104
- { ' ' }
105
161
< span className = { styles . dropZoneLink } > { translations . FileInputField . browse } </ span >
162
+ { isDragAndDropSupported && ` ${ translations . FileInputField . drop } ` }
106
163
</ >
107
164
) }
108
165
{ selectedFileNames . length === 1 && selectedFileNames [ 0 ] }
@@ -144,6 +201,11 @@ FileInputField.defaultProps = {
144
201
id : undefined ,
145
202
isLabelVisible : true ,
146
203
layout : 'vertical' ,
204
+ onChange : ( ) => { } ,
205
+ onDragEnter : ( ) => { } ,
206
+ onDragLeave : ( ) => { } ,
207
+ onDragOver : ( ) => { } ,
208
+ onDrop : ( ) => { } ,
147
209
required : false ,
148
210
size : 'medium' ,
149
211
validationState : null ,
@@ -190,6 +252,26 @@ FileInputField.propTypes = {
190
252
*
191
253
*/
192
254
layout : PropTypes . oneOf ( [ 'horizontal' , 'vertical' ] ) ,
255
+ /**
256
+ * Callback fired when the value of the input changes.
257
+ */
258
+ onChange : PropTypes . func ,
259
+ /**
260
+ * Callback fired when a drag event enters the field.
261
+ */
262
+ onDragEnter : PropTypes . func ,
263
+ /**
264
+ * Callback fired when a drag event leaves the field.
265
+ */
266
+ onDragLeave : PropTypes . func ,
267
+ /**
268
+ * Callback fired when a drag event is over the field.
269
+ */
270
+ onDragOver : PropTypes . func ,
271
+ /**
272
+ * Callback fired when a file is dropped onto the field.
273
+ */
274
+ onDrop : PropTypes . func ,
193
275
/**
194
276
* If `true`, the input will be required.
195
277
*/
0 commit comments