react-dropzone 11.5.1 → 11.7.0
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.
- package/.eslintignore +1 -3
- package/.eslintrc +1 -1
- package/.husky/commit-msg +5 -0
- package/.husky/pre-commit +5 -0
- package/.nvmrc +1 -1
- package/README.md +77 -29
- package/dist/es/index.js +157 -123
- package/dist/es/utils/index.js +39 -3
- package/dist/index.js +16 -2
- package/examples/events/README.md +3 -1
- package/examples/pintura/README.md +144 -0
- package/package.json +78 -75
- package/rollup.config.js +9 -6
- package/src/.eslintrc +7 -3
- package/src/__snapshots__/index.spec.js.snap +1 -1
- package/src/index.js +174 -133
- package/src/index.spec.js +240 -0
- package/src/utils/index.js +38 -4
- package/src/utils/index.spec.js +92 -29
- package/styleguide.config.js +2 -4
- package/typings/.eslintrc +28 -0
- package/examples/doka/README.md +0 -140
- package/tslint.json +0 -101
package/.eslintignore
CHANGED
package/.eslintrc
CHANGED
package/.nvmrc
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
14.17.0
|
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
3
|
# react-dropzone
|
|
4
|
-
|
|
5
4
|
[](https://www.npmjs.com/package/react-dropzone)
|
|
6
|
-
[](https://github.com/react-dropzone/react-dropzone/actions?query=workflow%3ATest)
|
|
7
6
|
[](https://codecov.io/gh/react-dropzone/react-dropzone)
|
|
8
|
-
[](#backers)
|
|
9
|
-
[](#sponsors)
|
|
10
|
-
[](#backers)
|
|
8
|
+
[](#sponsors)
|
|
9
|
+
[](https://gitpod.io/#https://github.com/react-dropzone/react-dropzone)
|
|
11
10
|
|
|
12
11
|
Simple React hook to create a HTML5-compliant drag'n'drop zone for files.
|
|
13
12
|
|
|
@@ -15,7 +14,6 @@ Documentation and examples at https://react-dropzone.js.org. Source code at http
|
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
## Installation
|
|
18
|
-
|
|
19
17
|
Install it from npm and include it in your React build process (using [Webpack](http://webpack.github.io/), [Browserify](http://browserify.org/), etc).
|
|
20
18
|
|
|
21
19
|
```bash
|
|
@@ -53,8 +51,6 @@ function MyDropzone() {
|
|
|
53
51
|
}
|
|
54
52
|
```
|
|
55
53
|
|
|
56
|
-
**IMPORTANT**: Under the hood, this lib makes use of [hooks](https://reactjs.org/docs/hooks-intro.html), therefore, using it requires React `>= 16.8`.
|
|
57
|
-
|
|
58
54
|
Or the wrapper component for the hook:
|
|
59
55
|
```jsx static
|
|
60
56
|
import React from 'react'
|
|
@@ -72,10 +68,7 @@ import Dropzone from 'react-dropzone'
|
|
|
72
68
|
</Dropzone>
|
|
73
69
|
```
|
|
74
70
|
|
|
75
|
-
|
|
76
|
-
**Warning**: On most recent browsers versions, the files given by `onDrop` won't have properties `path` or `fullPath`, see [this SO question](https://stackoverflow.com/a/23005925/2275818) and [this issue](https://github.com/react-dropzone/react-dropzone/issues/477).
|
|
77
|
-
|
|
78
|
-
Furthermore, if you want to access file contents you have to use the [FileReader API](https://developer.mozilla.org/en-US/docs/Web/API/FileReader):
|
|
71
|
+
If you want to access file contents you have to use the [FileReader API](https://developer.mozilla.org/en-US/docs/Web/API/FileReader):
|
|
79
72
|
|
|
80
73
|
```jsx static
|
|
81
74
|
import React, {useCallback} from 'react'
|
|
@@ -110,7 +103,6 @@ function MyDropzone() {
|
|
|
110
103
|
|
|
111
104
|
|
|
112
105
|
## Dropzone Props Getters
|
|
113
|
-
|
|
114
106
|
The dropzone property getters are just two functions that return objects with properties which you need to use to create the drag 'n' drop zone.
|
|
115
107
|
The root properties can be applied to whatever element you want, whereas the input properties must be applied to an `<input>`:
|
|
116
108
|
```jsx static
|
|
@@ -134,11 +126,13 @@ This is in order to avoid your props being overridden (or overriding the props r
|
|
|
134
126
|
```jsx static
|
|
135
127
|
<div
|
|
136
128
|
{...getRootProps({
|
|
137
|
-
onClick: event => console.log(event)
|
|
129
|
+
onClick: event => console.log(event),
|
|
130
|
+
role: 'button',
|
|
131
|
+
'aria-label': 'drag and drop area',
|
|
132
|
+
...
|
|
138
133
|
})}
|
|
139
134
|
/>
|
|
140
135
|
```
|
|
141
|
-
> ♿ this is also where you pass accessibility props like `role`, `aria-labelledby` ...etc.
|
|
142
136
|
|
|
143
137
|
In the example above, the provided `{onClick}` handler will be invoked before the internal one, therefore, internal callbacks can be prevented by simply using [stopPropagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation).
|
|
144
138
|
See [Events](https://react-dropzone.js.org#events) for more examples.
|
|
@@ -146,10 +140,9 @@ See [Events](https://react-dropzone.js.org#events) for more examples.
|
|
|
146
140
|
*Important*: if you omit rendering an `<input>` and/or binding the props from `getInputProps()`, opening a file dialog will not be possible.
|
|
147
141
|
|
|
148
142
|
## Refs
|
|
149
|
-
|
|
150
143
|
Both `getRootProps` and `getInputProps` accept a custom `refKey` (defaults to `ref`) as one of the attributes passed down in the parameter.
|
|
151
144
|
|
|
152
|
-
This can be useful when the element you're trying to apply the props from either one of those fns does not expose a reference to the element, e.g
|
|
145
|
+
This can be useful when the element you're trying to apply the props from either one of those fns does not expose a reference to the element, e.g:
|
|
153
146
|
|
|
154
147
|
```jsx static
|
|
155
148
|
import React from 'react'
|
|
@@ -170,7 +163,7 @@ function Example() {
|
|
|
170
163
|
}
|
|
171
164
|
```
|
|
172
165
|
|
|
173
|
-
If you're working with [Material UI](https://
|
|
166
|
+
If you're working with [Material UI v4](https://v4.mui.com/) and would like to apply the root props on some component that does not expose a ref, use [RootRef](https://v4.mui.com/api/root-ref/):
|
|
174
167
|
|
|
175
168
|
```jsx static
|
|
176
169
|
import React from 'react'
|
|
@@ -190,7 +183,7 @@ function PaperDropzone() {
|
|
|
190
183
|
}
|
|
191
184
|
```
|
|
192
185
|
|
|
193
|
-
|
|
186
|
+
**IMPORTANT**: do not set the `ref` prop on the elements where `getRootProps()`/`getInputProps()` props are set, instead, get the refs from the hook itself:
|
|
194
187
|
|
|
195
188
|
```jsx static
|
|
196
189
|
import React from 'react'
|
|
@@ -232,12 +225,11 @@ dropzoneRef.open()
|
|
|
232
225
|
|
|
233
226
|
|
|
234
227
|
## Testing
|
|
235
|
-
|
|
236
|
-
*Important*: `react-dropzone` makes some of its drag 'n' drop callbacks asynchronous to enable promise based `getFilesFromEvent()` functions. In order to test components that use this library, you may want to use the [react-testing-library](https://github.com/testing-library/react-testing-library):
|
|
228
|
+
`react-dropzone` makes some of its drag 'n' drop callbacks asynchronous to enable promise based `getFilesFromEvent()` functions. In order to test components that use this library, you need to use the [react-testing-library](https://github.com/testing-library/react-testing-library):
|
|
237
229
|
```js static
|
|
238
230
|
import React from 'react'
|
|
239
231
|
import Dropzone from 'react-dropzone'
|
|
240
|
-
import {
|
|
232
|
+
import {act, fireEvent, render, waitFor} from '@testing-library/react'
|
|
241
233
|
|
|
242
234
|
test('invoke onDragEnter when dragenter event occurs', async () => {
|
|
243
235
|
const file = new File([
|
|
@@ -289,22 +281,79 @@ function mockData(files) {
|
|
|
289
281
|
}
|
|
290
282
|
```
|
|
291
283
|
|
|
292
|
-
|
|
284
|
+
**NOTE**: using [Enzyme](https://airbnb.io/enzyme) for testing is not supported at the moment, see [#2011](https://github.com/airbnb/enzyme/issues/2011).
|
|
293
285
|
|
|
294
|
-
More examples for this can be found in `react-dropzone`s own [test suites](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.spec.js).
|
|
286
|
+
More examples for this can be found in `react-dropzone`'s own [test suites](https://github.com/react-dropzone/react-dropzone/blob/master/src/index.spec.js).
|
|
295
287
|
|
|
296
|
-
##
|
|
288
|
+
## Caveats
|
|
289
|
+
### Required React Version
|
|
290
|
+
React [16.8](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html) or above is required because we use [hooks](https://reactjs.org/docs/hooks-intro.html) (the lib itself is a hook).
|
|
297
291
|
|
|
292
|
+
### File Paths
|
|
293
|
+
Files returned by the hook or passed as arg to the `onDrop` cb won't have the properties `path` or `fullPath`.
|
|
294
|
+
For more inf check [this SO question](https://stackoverflow.com/a/23005925/2275818) and [this issue](https://github.com/react-dropzone/react-dropzone/issues/477).
|
|
298
295
|
|
|
296
|
+
### Not a File Uploader
|
|
297
|
+
This lib is not a file uploader; as such, it does not process files or provide any way to make HTTP requests to some server; if you're looking for that, checkout [filepond](https://pqina.nl/filepond) or [uppy.io](https://uppy.io/).
|
|
299
298
|
|
|
300
|
-
|
|
299
|
+
### Using \<label\> as Root
|
|
300
|
+
If you use [\<label\>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label) as the root element, the file dialog will be opened twice; see [#1107](https://github.com/react-dropzone/react-dropzone/issues/1107) why. To avoid this, use `noClick`:
|
|
301
|
+
```jsx static
|
|
302
|
+
import React, {useCallback} from 'react'
|
|
303
|
+
import {useDropzone} from 'react-dropzone'
|
|
301
304
|
|
|
302
|
-
|
|
305
|
+
function MyDropzone() {
|
|
306
|
+
const {getRootProps, getInputProps} = useDropzone({noClick: true})
|
|
303
307
|
|
|
304
|
-
|
|
308
|
+
return (
|
|
309
|
+
<label {...getRootProps()}>
|
|
310
|
+
<input {...getInputProps()} />
|
|
311
|
+
</label>
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Using open() on Click
|
|
317
|
+
If you bind a click event on an inner element and use `open()`, it will trigger a click on the root element too, resulting in the file dialog opening twice. To prevent this, use the `noClick` on the root:
|
|
318
|
+
```jsx static
|
|
319
|
+
import React, {useCallback} from 'react'
|
|
320
|
+
import {useDropzone} from 'react-dropzone'
|
|
305
321
|
|
|
322
|
+
function MyDropzone() {
|
|
323
|
+
const {getRootProps, getInputProps, open} = useDropzone({noClick: true})
|
|
324
|
+
|
|
325
|
+
return (
|
|
326
|
+
<div {...getRootProps()}>
|
|
327
|
+
<input {...getInputProps()} />
|
|
328
|
+
<button type="button" onClick={open}>
|
|
329
|
+
Open
|
|
330
|
+
</button>
|
|
331
|
+
</div>
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### File Dialog Cancel Callback
|
|
337
|
+
The `onFileDialogCancel()` cb is unstable in most browsers, meaning, there's a good chance of it being triggered even though you have selected files.
|
|
338
|
+
|
|
339
|
+
We rely on using a timeout of `300ms` after the window is focused (the window `onfocus` event is triggered when the file select dialog is closed) to check if any files were selected and trigger `onFileDialogCancel` if none were selected.
|
|
340
|
+
|
|
341
|
+
As one can imagine, this doesn't really work if there's a lot of files or large files as by the time we trigger the check, the browser is still processing the files and no `onchange` events are triggered yet on the input. Check [#1031](https://github.com/react-dropzone/react-dropzone/issues/1031) for more info.
|
|
342
|
+
|
|
343
|
+
Fortunately, there's the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API), which is currently a working draft and some browsers support it (see [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker#browser_compatibility)), that provides a reliable way to prompt the user for file selection and capture cancellation.
|
|
344
|
+
|
|
345
|
+
And this lib makes use of it if available. Though, there's a small catch: using file extensions for the `accept` property is not supported; you must use MIME types as described in [common MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types). Also check [accepting specific file types](https://react-dropzone.js.org/#section-accepting-specific-file-types) for more info on the subject of `accept` limitations.
|
|
346
|
+
|
|
347
|
+
## Supported Browsers
|
|
306
348
|
We use [browserslist](https://github.com/browserslist/browserslist) config to state the browser support for this lib, so check it out on [browserslist.dev](https://browserslist.dev/?q=ZGVmYXVsdHM%3D).
|
|
307
349
|
|
|
350
|
+
|
|
351
|
+
## Need image editing?
|
|
352
|
+
React Dropzone integrates perfectly with [Pintura Image Editor](https://pqina.nl/pintura/?ref=react-dropzone), creating a modern image editing experience. Pintura supports crop aspect ratios, resizing, rotating, cropping, annotating, filtering, and much more.
|
|
353
|
+
|
|
354
|
+
Checkout the [Pintura integration example](https://codesandbox.io/s/react-dropzone-pintura-40xh4?file=/src/App.js).
|
|
355
|
+
|
|
356
|
+
|
|
308
357
|
## Support
|
|
309
358
|
|
|
310
359
|
### Backers
|
|
@@ -378,5 +427,4 @@ Become a sponsor and get your logo on our README on Github with a link to your s
|
|
|
378
427
|
|
|
379
428
|
|
|
380
429
|
## License
|
|
381
|
-
|
|
382
430
|
MIT
|
package/dist/es/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var _excluded = ["children"],
|
|
2
2
|
_excluded2 = ["open"],
|
|
3
|
-
_excluded3 = ["refKey", "onKeyDown", "onFocus", "onBlur", "onClick", "onDragEnter", "onDragOver", "onDragLeave", "onDrop"],
|
|
3
|
+
_excluded3 = ["refKey", "role", "onKeyDown", "onFocus", "onBlur", "onClick", "onDragEnter", "onDragOver", "onDragLeave", "onDrop"],
|
|
4
4
|
_excluded4 = ["refKey", "onChange", "onClick"];
|
|
5
5
|
|
|
6
6
|
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
@@ -23,9 +23,9 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
|
|
|
23
23
|
|
|
24
24
|
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
25
25
|
|
|
26
|
-
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object);
|
|
26
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
27
27
|
|
|
28
|
-
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]
|
|
28
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
29
29
|
|
|
30
30
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
31
31
|
|
|
@@ -37,7 +37,7 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
|
|
|
37
37
|
import React, { forwardRef, Fragment, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef } from 'react';
|
|
38
38
|
import PropTypes from 'prop-types';
|
|
39
39
|
import { fromEvent } from 'file-selector';
|
|
40
|
-
import { allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, isEvtWithFiles, isIeOrEdge, isPropagationStopped, onDocumentDragOver, TOO_MANY_FILES_REJECTION } from './utils/index';
|
|
40
|
+
import { allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, filePickerOptionsTypes, canUseFileSystemAccessAPI, isEvtWithFiles, isIeOrEdge, isPropagationStopped, onDocumentDragOver, TOO_MANY_FILES_REJECTION } from './utils/index';
|
|
41
41
|
/**
|
|
42
42
|
* Convenience wrapper component for the `useDropzone` hook
|
|
43
43
|
*
|
|
@@ -439,6 +439,12 @@ export function useDropzone() {
|
|
|
439
439
|
noDragEventsBubbling = _defaultProps$options.noDragEventsBubbling,
|
|
440
440
|
validator = _defaultProps$options.validator;
|
|
441
441
|
|
|
442
|
+
var onFileDialogOpenCb = useMemo(function () {
|
|
443
|
+
return typeof onFileDialogOpen === 'function' ? onFileDialogOpen : noop;
|
|
444
|
+
}, [onFileDialogOpen]);
|
|
445
|
+
var onFileDialogCancelCb = useMemo(function () {
|
|
446
|
+
return typeof onFileDialogCancel === 'function' ? onFileDialogCancel : noop;
|
|
447
|
+
}, [onFileDialogCancel]);
|
|
442
448
|
var rootRef = useRef(null);
|
|
443
449
|
var inputRef = useRef(null);
|
|
444
450
|
|
|
@@ -449,22 +455,7 @@ export function useDropzone() {
|
|
|
449
455
|
|
|
450
456
|
var isFocused = state.isFocused,
|
|
451
457
|
isFileDialogActive = state.isFileDialogActive,
|
|
452
|
-
draggedFiles = state.draggedFiles; //
|
|
453
|
-
|
|
454
|
-
var openFileDialog = useCallback(function () {
|
|
455
|
-
if (inputRef.current) {
|
|
456
|
-
dispatch({
|
|
457
|
-
type: 'openDialog'
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
if (typeof onFileDialogOpen === 'function') {
|
|
461
|
-
onFileDialogOpen();
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
inputRef.current.value = null;
|
|
465
|
-
inputRef.current.click();
|
|
466
|
-
}
|
|
467
|
-
}, [dispatch, onFileDialogOpen]); // Update file dialog active state when the window is focused on
|
|
458
|
+
draggedFiles = state.draggedFiles; // Update file dialog active state when the window is focused on
|
|
468
459
|
|
|
469
460
|
var onWindowFocus = function onWindowFocus() {
|
|
470
461
|
// Execute the timeout only if the file dialog is opened in the browser
|
|
@@ -477,10 +468,7 @@ export function useDropzone() {
|
|
|
477
468
|
dispatch({
|
|
478
469
|
type: 'closeDialog'
|
|
479
470
|
});
|
|
480
|
-
|
|
481
|
-
if (typeof onFileDialogCancel === 'function') {
|
|
482
|
-
onFileDialogCancel();
|
|
483
|
-
}
|
|
471
|
+
onFileDialogCancelCb();
|
|
484
472
|
}
|
|
485
473
|
}
|
|
486
474
|
}, 300);
|
|
@@ -488,49 +476,15 @@ export function useDropzone() {
|
|
|
488
476
|
};
|
|
489
477
|
|
|
490
478
|
useEffect(function () {
|
|
479
|
+
if (canUseFileSystemAccessAPI()) {
|
|
480
|
+
return function () {};
|
|
481
|
+
}
|
|
482
|
+
|
|
491
483
|
window.addEventListener('focus', onWindowFocus, false);
|
|
492
484
|
return function () {
|
|
493
485
|
window.removeEventListener('focus', onWindowFocus, false);
|
|
494
486
|
};
|
|
495
|
-
}, [inputRef, isFileDialogActive,
|
|
496
|
-
|
|
497
|
-
var onKeyDownCb = useCallback(function (event) {
|
|
498
|
-
// Ignore keyboard events bubbling up the DOM tree
|
|
499
|
-
if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) {
|
|
500
|
-
return;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
if (event.keyCode === 32 || event.keyCode === 13) {
|
|
504
|
-
event.preventDefault();
|
|
505
|
-
openFileDialog();
|
|
506
|
-
}
|
|
507
|
-
}, [rootRef, inputRef, openFileDialog]); // Update focus state for the dropzone
|
|
508
|
-
|
|
509
|
-
var onFocusCb = useCallback(function () {
|
|
510
|
-
dispatch({
|
|
511
|
-
type: 'focus'
|
|
512
|
-
});
|
|
513
|
-
}, []);
|
|
514
|
-
var onBlurCb = useCallback(function () {
|
|
515
|
-
dispatch({
|
|
516
|
-
type: 'blur'
|
|
517
|
-
});
|
|
518
|
-
}, []); // Cb to open the file dialog when click occurs on the dropzone
|
|
519
|
-
|
|
520
|
-
var onClickCb = useCallback(function () {
|
|
521
|
-
if (noClick) {
|
|
522
|
-
return;
|
|
523
|
-
} // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
|
|
524
|
-
// to ensure React can handle state changes
|
|
525
|
-
// See: https://github.com/react-dropzone/react-dropzone/issues/450
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (isIeOrEdge()) {
|
|
529
|
-
setTimeout(openFileDialog, 0);
|
|
530
|
-
} else {
|
|
531
|
-
openFileDialog();
|
|
532
|
-
}
|
|
533
|
-
}, [inputRef, noClick, openFileDialog]);
|
|
487
|
+
}, [inputRef, isFileDialogActive, onFileDialogCancelCb]);
|
|
534
488
|
var dragTargetsRef = useRef([]);
|
|
535
489
|
|
|
536
490
|
var onDocumentDrop = function onDocumentDrop(event) {
|
|
@@ -633,6 +587,69 @@ export function useDropzone() {
|
|
|
633
587
|
onDragLeave(event);
|
|
634
588
|
}
|
|
635
589
|
}, [rootRef, onDragLeave, noDragEventsBubbling]);
|
|
590
|
+
var setFiles = useCallback(function (files, event) {
|
|
591
|
+
var acceptedFiles = [];
|
|
592
|
+
var fileRejections = [];
|
|
593
|
+
files.forEach(function (file) {
|
|
594
|
+
var _fileAccepted = fileAccepted(file, accept),
|
|
595
|
+
_fileAccepted2 = _slicedToArray(_fileAccepted, 2),
|
|
596
|
+
accepted = _fileAccepted2[0],
|
|
597
|
+
acceptError = _fileAccepted2[1];
|
|
598
|
+
|
|
599
|
+
var _fileMatchSize = fileMatchSize(file, minSize, maxSize),
|
|
600
|
+
_fileMatchSize2 = _slicedToArray(_fileMatchSize, 2),
|
|
601
|
+
sizeMatch = _fileMatchSize2[0],
|
|
602
|
+
sizeError = _fileMatchSize2[1];
|
|
603
|
+
|
|
604
|
+
var customErrors = validator ? validator(file) : null;
|
|
605
|
+
|
|
606
|
+
if (accepted && sizeMatch && !customErrors) {
|
|
607
|
+
acceptedFiles.push(file);
|
|
608
|
+
} else {
|
|
609
|
+
var errors = [acceptError, sizeError];
|
|
610
|
+
|
|
611
|
+
if (customErrors) {
|
|
612
|
+
errors = errors.concat(customErrors);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
fileRejections.push({
|
|
616
|
+
file: file,
|
|
617
|
+
errors: errors.filter(function (e) {
|
|
618
|
+
return e;
|
|
619
|
+
})
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
if (!multiple && acceptedFiles.length > 1 || multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) {
|
|
625
|
+
// Reject everything and empty accepted files
|
|
626
|
+
acceptedFiles.forEach(function (file) {
|
|
627
|
+
fileRejections.push({
|
|
628
|
+
file: file,
|
|
629
|
+
errors: [TOO_MANY_FILES_REJECTION]
|
|
630
|
+
});
|
|
631
|
+
});
|
|
632
|
+
acceptedFiles.splice(0);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
dispatch({
|
|
636
|
+
acceptedFiles: acceptedFiles,
|
|
637
|
+
fileRejections: fileRejections,
|
|
638
|
+
type: 'setFiles'
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
if (onDrop) {
|
|
642
|
+
onDrop(acceptedFiles, fileRejections, event);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (fileRejections.length > 0 && onDropRejected) {
|
|
646
|
+
onDropRejected(fileRejections, event);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (acceptedFiles.length > 0 && onDropAccepted) {
|
|
650
|
+
onDropAccepted(acceptedFiles, event);
|
|
651
|
+
}
|
|
652
|
+
}, [dispatch, multiple, accept, minSize, maxSize, maxFiles, onDrop, onDropAccepted, onDropRejected, validator]);
|
|
636
653
|
var onDropCb = useCallback(function (event) {
|
|
637
654
|
event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
|
|
638
655
|
|
|
@@ -646,74 +663,87 @@ export function useDropzone() {
|
|
|
646
663
|
return;
|
|
647
664
|
}
|
|
648
665
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
var _fileAccepted = fileAccepted(file, accept),
|
|
653
|
-
_fileAccepted2 = _slicedToArray(_fileAccepted, 2),
|
|
654
|
-
accepted = _fileAccepted2[0],
|
|
655
|
-
acceptError = _fileAccepted2[1];
|
|
656
|
-
|
|
657
|
-
var _fileMatchSize = fileMatchSize(file, minSize, maxSize),
|
|
658
|
-
_fileMatchSize2 = _slicedToArray(_fileMatchSize, 2),
|
|
659
|
-
sizeMatch = _fileMatchSize2[0],
|
|
660
|
-
sizeError = _fileMatchSize2[1];
|
|
661
|
-
|
|
662
|
-
var customErrors = validator ? validator(file) : null;
|
|
663
|
-
|
|
664
|
-
if (accepted && sizeMatch && !customErrors) {
|
|
665
|
-
acceptedFiles.push(file);
|
|
666
|
-
} else {
|
|
667
|
-
var errors = [acceptError, sizeError];
|
|
668
|
-
|
|
669
|
-
if (customErrors) {
|
|
670
|
-
errors = errors.concat(customErrors);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
fileRejections.push({
|
|
674
|
-
file: file,
|
|
675
|
-
errors: errors.filter(function (e) {
|
|
676
|
-
return e;
|
|
677
|
-
})
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
});
|
|
666
|
+
setFiles(files, event);
|
|
667
|
+
});
|
|
668
|
+
}
|
|
681
669
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
file: file,
|
|
687
|
-
errors: [TOO_MANY_FILES_REJECTION]
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
acceptedFiles.splice(0);
|
|
691
|
-
}
|
|
670
|
+
dispatch({
|
|
671
|
+
type: 'reset'
|
|
672
|
+
});
|
|
673
|
+
}, [getFilesFromEvent, setFiles, noDragEventsBubbling]); // Fn for opening the file dialog programmatically
|
|
692
674
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
675
|
+
var openFileDialog = useCallback(function () {
|
|
676
|
+
if (canUseFileSystemAccessAPI()) {
|
|
677
|
+
dispatch({
|
|
678
|
+
type: 'openDialog'
|
|
679
|
+
});
|
|
680
|
+
onFileDialogOpenCb(); // https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
|
681
|
+
|
|
682
|
+
var opts = {
|
|
683
|
+
multiple: multiple,
|
|
684
|
+
types: filePickerOptionsTypes(accept)
|
|
685
|
+
};
|
|
686
|
+
window.showOpenFilePicker(opts).then(function (handles) {
|
|
687
|
+
return getFilesFromEvent(handles);
|
|
688
|
+
}).then(function (files) {
|
|
689
|
+
return setFiles(files, null);
|
|
690
|
+
}).catch(function (e) {
|
|
691
|
+
return onFileDialogCancelCb(e);
|
|
692
|
+
}).finally(function () {
|
|
693
|
+
return dispatch({
|
|
694
|
+
type: 'closeDialog'
|
|
697
695
|
});
|
|
696
|
+
});
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
698
699
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
700
|
+
if (inputRef.current) {
|
|
701
|
+
dispatch({
|
|
702
|
+
type: 'openDialog'
|
|
703
|
+
});
|
|
704
|
+
onFileDialogOpenCb();
|
|
705
|
+
inputRef.current.value = null;
|
|
706
|
+
inputRef.current.click();
|
|
707
|
+
}
|
|
708
|
+
}, [dispatch, onFileDialogOpenCb, onFileDialogCancelCb, setFiles, accept, multiple]); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
|
|
702
709
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
710
|
+
var onKeyDownCb = useCallback(function (event) {
|
|
711
|
+
// Ignore keyboard events bubbling up the DOM tree
|
|
712
|
+
if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
706
715
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
});
|
|
716
|
+
if (event.keyCode === 32 || event.keyCode === 13) {
|
|
717
|
+
event.preventDefault();
|
|
718
|
+
openFileDialog();
|
|
711
719
|
}
|
|
720
|
+
}, [rootRef, inputRef, openFileDialog]); // Update focus state for the dropzone
|
|
712
721
|
|
|
722
|
+
var onFocusCb = useCallback(function () {
|
|
713
723
|
dispatch({
|
|
714
|
-
type: '
|
|
724
|
+
type: 'focus'
|
|
725
|
+
});
|
|
726
|
+
}, []);
|
|
727
|
+
var onBlurCb = useCallback(function () {
|
|
728
|
+
dispatch({
|
|
729
|
+
type: 'blur'
|
|
715
730
|
});
|
|
716
|
-
}, [
|
|
731
|
+
}, []); // Cb to open the file dialog when click occurs on the dropzone
|
|
732
|
+
|
|
733
|
+
var onClickCb = useCallback(function () {
|
|
734
|
+
if (noClick) {
|
|
735
|
+
return;
|
|
736
|
+
} // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
|
|
737
|
+
// to ensure React can handle state changes
|
|
738
|
+
// See: https://github.com/react-dropzone/react-dropzone/issues/450
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
if (isIeOrEdge()) {
|
|
742
|
+
setTimeout(openFileDialog, 0);
|
|
743
|
+
} else {
|
|
744
|
+
openFileDialog();
|
|
745
|
+
}
|
|
746
|
+
}, [inputRef, noClick, openFileDialog]);
|
|
717
747
|
|
|
718
748
|
var composeHandler = function composeHandler(fn) {
|
|
719
749
|
return disabled ? null : fn;
|
|
@@ -738,6 +768,7 @@ export function useDropzone() {
|
|
|
738
768
|
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
739
769
|
_ref2$refKey = _ref2.refKey,
|
|
740
770
|
refKey = _ref2$refKey === void 0 ? 'ref' : _ref2$refKey,
|
|
771
|
+
role = _ref2.role,
|
|
741
772
|
onKeyDown = _ref2.onKeyDown,
|
|
742
773
|
onFocus = _ref2.onFocus,
|
|
743
774
|
onBlur = _ref2.onBlur,
|
|
@@ -756,7 +787,8 @@ export function useDropzone() {
|
|
|
756
787
|
onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)),
|
|
757
788
|
onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)),
|
|
758
789
|
onDragLeave: composeDragHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)),
|
|
759
|
-
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb))
|
|
790
|
+
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)),
|
|
791
|
+
role: typeof role === 'string' && role !== '' ? role : 'button'
|
|
760
792
|
}, refKey, rootRef), !disabled && !noKeyboard ? {
|
|
761
793
|
tabIndex: 0
|
|
762
794
|
} : {}), rest);
|
|
@@ -826,7 +858,7 @@ function reducer(state, action) {
|
|
|
826
858
|
});
|
|
827
859
|
|
|
828
860
|
case 'openDialog':
|
|
829
|
-
return _objectSpread(_objectSpread({},
|
|
861
|
+
return _objectSpread(_objectSpread({}, initialState), {}, {
|
|
830
862
|
isFileDialogActive: true
|
|
831
863
|
});
|
|
832
864
|
|
|
@@ -858,4 +890,6 @@ function reducer(state, action) {
|
|
|
858
890
|
}
|
|
859
891
|
}
|
|
860
892
|
|
|
893
|
+
function noop() {}
|
|
894
|
+
|
|
861
895
|
export { ErrorCode } from './utils';
|