react-dropzone 11.5.2 → 11.7.1
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 +168 -124
- package/dist/es/utils/index.js +39 -3
- package/dist/index.js +16 -2
- package/examples/events/README.md +3 -1
- package/examples/no-jsx/README.md +32 -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 +186 -134
- package/src/index.spec.js +306 -0
- package/src/utils/index.js +38 -4
- package/src/utils/index.spec.js +92 -29
- package/styleguide.config.js +4 -0
- package/typings/.eslintrc +28 -0
- 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
|
*
|
|
@@ -85,7 +85,8 @@ var defaultProps = {
|
|
|
85
85
|
noKeyboard: false,
|
|
86
86
|
noDrag: false,
|
|
87
87
|
noDragEventsBubbling: false,
|
|
88
|
-
validator: null
|
|
88
|
+
validator: null,
|
|
89
|
+
useFsAccessApi: false
|
|
89
90
|
};
|
|
90
91
|
Dropzone.defaultProps = defaultProps;
|
|
91
92
|
Dropzone.propTypes = {
|
|
@@ -186,6 +187,12 @@ Dropzone.propTypes = {
|
|
|
186
187
|
*/
|
|
187
188
|
onFileDialogOpen: PropTypes.func,
|
|
188
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
|
|
192
|
+
* to open the file picker instead of using an `<input type="file">` click event.
|
|
193
|
+
*/
|
|
194
|
+
useFsAccessApi: PropTypes.bool,
|
|
195
|
+
|
|
189
196
|
/**
|
|
190
197
|
* Cb for when the `dragenter` event occurs.
|
|
191
198
|
*
|
|
@@ -379,6 +386,8 @@ var initialState = {
|
|
|
379
386
|
* @param {boolean} [props.disabled=false] Enable/disable the dropzone
|
|
380
387
|
* @param {getFilesFromEvent} [props.getFilesFromEvent] Use this to provide a custom file aggregator
|
|
381
388
|
* @param {Function} [props.onFileDialogCancel] Cb for when closing the file dialog with no selection
|
|
389
|
+
* @param {boolean} [props.useFsAccessApi] Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
|
|
390
|
+
* to open the file picker instead of using an `<input type="file">` click event.
|
|
382
391
|
* @param {Function} [props.onFileDialogOpen] Cb for when opening the file dialog
|
|
383
392
|
* @param {dragCb} [props.onDragEnter] Cb for when the `dragenter` event occurs.
|
|
384
393
|
* @param {dragCb} [props.onDragLeave] Cb for when the `dragleave` event occurs
|
|
@@ -432,6 +441,7 @@ export function useDropzone() {
|
|
|
432
441
|
onDropRejected = _defaultProps$options.onDropRejected,
|
|
433
442
|
onFileDialogCancel = _defaultProps$options.onFileDialogCancel,
|
|
434
443
|
onFileDialogOpen = _defaultProps$options.onFileDialogOpen,
|
|
444
|
+
useFsAccessApi = _defaultProps$options.useFsAccessApi,
|
|
435
445
|
preventDropOnDocument = _defaultProps$options.preventDropOnDocument,
|
|
436
446
|
noClick = _defaultProps$options.noClick,
|
|
437
447
|
noKeyboard = _defaultProps$options.noKeyboard,
|
|
@@ -439,6 +449,12 @@ export function useDropzone() {
|
|
|
439
449
|
noDragEventsBubbling = _defaultProps$options.noDragEventsBubbling,
|
|
440
450
|
validator = _defaultProps$options.validator;
|
|
441
451
|
|
|
452
|
+
var onFileDialogOpenCb = useMemo(function () {
|
|
453
|
+
return typeof onFileDialogOpen === 'function' ? onFileDialogOpen : noop;
|
|
454
|
+
}, [onFileDialogOpen]);
|
|
455
|
+
var onFileDialogCancelCb = useMemo(function () {
|
|
456
|
+
return typeof onFileDialogCancel === 'function' ? onFileDialogCancel : noop;
|
|
457
|
+
}, [onFileDialogCancel]);
|
|
442
458
|
var rootRef = useRef(null);
|
|
443
459
|
var inputRef = useRef(null);
|
|
444
460
|
|
|
@@ -449,22 +465,7 @@ export function useDropzone() {
|
|
|
449
465
|
|
|
450
466
|
var isFocused = state.isFocused,
|
|
451
467
|
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
|
|
468
|
+
draggedFiles = state.draggedFiles; // Update file dialog active state when the window is focused on
|
|
468
469
|
|
|
469
470
|
var onWindowFocus = function onWindowFocus() {
|
|
470
471
|
// Execute the timeout only if the file dialog is opened in the browser
|
|
@@ -477,10 +478,7 @@ export function useDropzone() {
|
|
|
477
478
|
dispatch({
|
|
478
479
|
type: 'closeDialog'
|
|
479
480
|
});
|
|
480
|
-
|
|
481
|
-
if (typeof onFileDialogCancel === 'function') {
|
|
482
|
-
onFileDialogCancel();
|
|
483
|
-
}
|
|
481
|
+
onFileDialogCancelCb();
|
|
484
482
|
}
|
|
485
483
|
}
|
|
486
484
|
}, 300);
|
|
@@ -488,49 +486,15 @@ export function useDropzone() {
|
|
|
488
486
|
};
|
|
489
487
|
|
|
490
488
|
useEffect(function () {
|
|
489
|
+
if (useFsAccessApi && canUseFileSystemAccessAPI()) {
|
|
490
|
+
return function () {};
|
|
491
|
+
}
|
|
492
|
+
|
|
491
493
|
window.addEventListener('focus', onWindowFocus, false);
|
|
492
494
|
return function () {
|
|
493
495
|
window.removeEventListener('focus', onWindowFocus, false);
|
|
494
496
|
};
|
|
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]);
|
|
497
|
+
}, [inputRef, isFileDialogActive, onFileDialogCancelCb, useFsAccessApi]);
|
|
534
498
|
var dragTargetsRef = useRef([]);
|
|
535
499
|
|
|
536
500
|
var onDocumentDrop = function onDocumentDrop(event) {
|
|
@@ -633,6 +597,69 @@ export function useDropzone() {
|
|
|
633
597
|
onDragLeave(event);
|
|
634
598
|
}
|
|
635
599
|
}, [rootRef, onDragLeave, noDragEventsBubbling]);
|
|
600
|
+
var setFiles = useCallback(function (files, event) {
|
|
601
|
+
var acceptedFiles = [];
|
|
602
|
+
var fileRejections = [];
|
|
603
|
+
files.forEach(function (file) {
|
|
604
|
+
var _fileAccepted = fileAccepted(file, accept),
|
|
605
|
+
_fileAccepted2 = _slicedToArray(_fileAccepted, 2),
|
|
606
|
+
accepted = _fileAccepted2[0],
|
|
607
|
+
acceptError = _fileAccepted2[1];
|
|
608
|
+
|
|
609
|
+
var _fileMatchSize = fileMatchSize(file, minSize, maxSize),
|
|
610
|
+
_fileMatchSize2 = _slicedToArray(_fileMatchSize, 2),
|
|
611
|
+
sizeMatch = _fileMatchSize2[0],
|
|
612
|
+
sizeError = _fileMatchSize2[1];
|
|
613
|
+
|
|
614
|
+
var customErrors = validator ? validator(file) : null;
|
|
615
|
+
|
|
616
|
+
if (accepted && sizeMatch && !customErrors) {
|
|
617
|
+
acceptedFiles.push(file);
|
|
618
|
+
} else {
|
|
619
|
+
var errors = [acceptError, sizeError];
|
|
620
|
+
|
|
621
|
+
if (customErrors) {
|
|
622
|
+
errors = errors.concat(customErrors);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
fileRejections.push({
|
|
626
|
+
file: file,
|
|
627
|
+
errors: errors.filter(function (e) {
|
|
628
|
+
return e;
|
|
629
|
+
})
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
if (!multiple && acceptedFiles.length > 1 || multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) {
|
|
635
|
+
// Reject everything and empty accepted files
|
|
636
|
+
acceptedFiles.forEach(function (file) {
|
|
637
|
+
fileRejections.push({
|
|
638
|
+
file: file,
|
|
639
|
+
errors: [TOO_MANY_FILES_REJECTION]
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
acceptedFiles.splice(0);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
dispatch({
|
|
646
|
+
acceptedFiles: acceptedFiles,
|
|
647
|
+
fileRejections: fileRejections,
|
|
648
|
+
type: 'setFiles'
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
if (onDrop) {
|
|
652
|
+
onDrop(acceptedFiles, fileRejections, event);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (fileRejections.length > 0 && onDropRejected) {
|
|
656
|
+
onDropRejected(fileRejections, event);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (acceptedFiles.length > 0 && onDropAccepted) {
|
|
660
|
+
onDropAccepted(acceptedFiles, event);
|
|
661
|
+
}
|
|
662
|
+
}, [dispatch, multiple, accept, minSize, maxSize, maxFiles, onDrop, onDropAccepted, onDropRejected, validator]);
|
|
636
663
|
var onDropCb = useCallback(function (event) {
|
|
637
664
|
event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
|
|
638
665
|
|
|
@@ -646,74 +673,87 @@ export function useDropzone() {
|
|
|
646
673
|
return;
|
|
647
674
|
}
|
|
648
675
|
|
|
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
|
-
});
|
|
676
|
+
setFiles(files, event);
|
|
677
|
+
});
|
|
678
|
+
}
|
|
681
679
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
file: file,
|
|
687
|
-
errors: [TOO_MANY_FILES_REJECTION]
|
|
688
|
-
});
|
|
689
|
-
});
|
|
690
|
-
acceptedFiles.splice(0);
|
|
691
|
-
}
|
|
680
|
+
dispatch({
|
|
681
|
+
type: 'reset'
|
|
682
|
+
});
|
|
683
|
+
}, [getFilesFromEvent, setFiles, noDragEventsBubbling]); // Fn for opening the file dialog programmatically
|
|
692
684
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
685
|
+
var openFileDialog = useCallback(function () {
|
|
686
|
+
if (useFsAccessApi && canUseFileSystemAccessAPI()) {
|
|
687
|
+
dispatch({
|
|
688
|
+
type: 'openDialog'
|
|
689
|
+
});
|
|
690
|
+
onFileDialogOpenCb(); // https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
|
691
|
+
|
|
692
|
+
var opts = {
|
|
693
|
+
multiple: multiple,
|
|
694
|
+
types: filePickerOptionsTypes(accept)
|
|
695
|
+
};
|
|
696
|
+
window.showOpenFilePicker(opts).then(function (handles) {
|
|
697
|
+
return getFilesFromEvent(handles);
|
|
698
|
+
}).then(function (files) {
|
|
699
|
+
return setFiles(files, null);
|
|
700
|
+
}).catch(function (e) {
|
|
701
|
+
return onFileDialogCancelCb(e);
|
|
702
|
+
}).finally(function () {
|
|
703
|
+
return dispatch({
|
|
704
|
+
type: 'closeDialog'
|
|
697
705
|
});
|
|
706
|
+
});
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
698
709
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
710
|
+
if (inputRef.current) {
|
|
711
|
+
dispatch({
|
|
712
|
+
type: 'openDialog'
|
|
713
|
+
});
|
|
714
|
+
onFileDialogOpenCb();
|
|
715
|
+
inputRef.current.value = null;
|
|
716
|
+
inputRef.current.click();
|
|
717
|
+
}
|
|
718
|
+
}, [dispatch, onFileDialogOpenCb, onFileDialogCancelCb, useFsAccessApi, setFiles, accept, multiple]); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
|
|
702
719
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
720
|
+
var onKeyDownCb = useCallback(function (event) {
|
|
721
|
+
// Ignore keyboard events bubbling up the DOM tree
|
|
722
|
+
if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
706
725
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
});
|
|
726
|
+
if (event.keyCode === 32 || event.keyCode === 13) {
|
|
727
|
+
event.preventDefault();
|
|
728
|
+
openFileDialog();
|
|
711
729
|
}
|
|
730
|
+
}, [rootRef, inputRef, openFileDialog]); // Update focus state for the dropzone
|
|
712
731
|
|
|
732
|
+
var onFocusCb = useCallback(function () {
|
|
713
733
|
dispatch({
|
|
714
|
-
type: '
|
|
734
|
+
type: 'focus'
|
|
715
735
|
});
|
|
716
|
-
}, [
|
|
736
|
+
}, []);
|
|
737
|
+
var onBlurCb = useCallback(function () {
|
|
738
|
+
dispatch({
|
|
739
|
+
type: 'blur'
|
|
740
|
+
});
|
|
741
|
+
}, []); // Cb to open the file dialog when click occurs on the dropzone
|
|
742
|
+
|
|
743
|
+
var onClickCb = useCallback(function () {
|
|
744
|
+
if (noClick) {
|
|
745
|
+
return;
|
|
746
|
+
} // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
|
|
747
|
+
// to ensure React can handle state changes
|
|
748
|
+
// See: https://github.com/react-dropzone/react-dropzone/issues/450
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
if (isIeOrEdge()) {
|
|
752
|
+
setTimeout(openFileDialog, 0);
|
|
753
|
+
} else {
|
|
754
|
+
openFileDialog();
|
|
755
|
+
}
|
|
756
|
+
}, [inputRef, noClick, openFileDialog]);
|
|
717
757
|
|
|
718
758
|
var composeHandler = function composeHandler(fn) {
|
|
719
759
|
return disabled ? null : fn;
|
|
@@ -738,6 +778,7 @@ export function useDropzone() {
|
|
|
738
778
|
var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
|
|
739
779
|
_ref2$refKey = _ref2.refKey,
|
|
740
780
|
refKey = _ref2$refKey === void 0 ? 'ref' : _ref2$refKey,
|
|
781
|
+
role = _ref2.role,
|
|
741
782
|
onKeyDown = _ref2.onKeyDown,
|
|
742
783
|
onFocus = _ref2.onFocus,
|
|
743
784
|
onBlur = _ref2.onBlur,
|
|
@@ -756,7 +797,8 @@ export function useDropzone() {
|
|
|
756
797
|
onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)),
|
|
757
798
|
onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)),
|
|
758
799
|
onDragLeave: composeDragHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)),
|
|
759
|
-
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb))
|
|
800
|
+
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)),
|
|
801
|
+
role: typeof role === 'string' && role !== '' ? role : 'button'
|
|
760
802
|
}, refKey, rootRef), !disabled && !noKeyboard ? {
|
|
761
803
|
tabIndex: 0
|
|
762
804
|
} : {}), rest);
|
|
@@ -826,7 +868,7 @@ function reducer(state, action) {
|
|
|
826
868
|
});
|
|
827
869
|
|
|
828
870
|
case 'openDialog':
|
|
829
|
-
return _objectSpread(_objectSpread({},
|
|
871
|
+
return _objectSpread(_objectSpread({}, initialState), {}, {
|
|
830
872
|
isFileDialogActive: true
|
|
831
873
|
});
|
|
832
874
|
|
|
@@ -858,4 +900,6 @@ function reducer(state, action) {
|
|
|
858
900
|
}
|
|
859
901
|
}
|
|
860
902
|
|
|
903
|
+
function noop() {}
|
|
904
|
+
|
|
861
905
|
export { ErrorCode } from './utils';
|