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 CHANGED
@@ -1,5 +1,3 @@
1
- dist/
2
1
  coverage/
3
- typings/*
2
+ dist/
4
3
  styleguide/*
5
- !.babelrc.js
package/.eslintrc CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "parser": "babel-eslint",
2
+ "parser": "@babel/eslint-parser",
3
3
  "parserOptions": {
4
4
  "ecmaVersion": 2017,
5
5
  "sourceType": "module"
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ # Check commit msg format
5
+ yarn commitlint --edit $1
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ # Check whatever is staged for issues
5
+ yarn lint-staged
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- 12.18
1
+ 14.17.0
package/README.md CHANGED
@@ -1,13 +1,12 @@
1
1
  ![react-dropzone logo](https://raw.githubusercontent.com/react-dropzone/react-dropzone/master/logo/logo.png)
2
2
 
3
3
  # react-dropzone
4
-
5
4
  [![npm](https://img.shields.io/npm/v/react-dropzone.svg?style=flat-square)](https://www.npmjs.com/package/react-dropzone)
6
- [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/react-dropzone/react-dropzone/Test?label=tests&style=flat-square)](https://github.com/react-dropzone/react-dropzone/actions?query=workflow%3ATest)
5
+ [![Tests](https://img.shields.io/github/workflow/status/react-dropzone/react-dropzone/Test?label=tests&style=flat-square)](https://github.com/react-dropzone/react-dropzone/actions?query=workflow%3ATest)
7
6
  [![codecov](https://img.shields.io/codecov/c/gh/react-dropzone/react-dropzone/master.svg?style=flat-square)](https://codecov.io/gh/react-dropzone/react-dropzone)
8
- [![Open Collective](https://img.shields.io/opencollective/backers/react-dropzone.svg?style=flat-square)](#backers)
9
- [![Open Collective](https://img.shields.io/opencollective/sponsors/react-dropzone.svg?style=flat-square)](#sponsors)
10
- [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/react-dropzone/react-dropzone)
7
+ [![Open Collective Backers](https://img.shields.io/opencollective/backers/react-dropzone.svg?style=flat-square)](#backers)
8
+ [![Open Collective Sponsors](https://img.shields.io/opencollective/sponsors/react-dropzone.svg?style=flat-square)](#sponsors)
9
+ [![Gitpod](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod&style=flat-square)](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://material-ui.com) and would like to apply the root props on some component that does not expose a ref, use [RootRef](https://material-ui.com/api/root-ref):
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
- *Important*: do not set the `ref` prop on the elements where `getRootProps()`/`getInputProps()` props are set, instead, get the refs from the hook itself:
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 { act, fireEvent, render, waitFor } from '@testing-library/react'
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
- *Note*: using [Enzyme](https://airbnb.io/enzyme) for testing is not supported at the moment, see [#2011](https://github.com/airbnb/enzyme/issues/2011).
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
- ## Need image editing?
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
- 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.
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
- Checkout the [Pintura integration example](https://codesandbox.io/s/react-dropzone-pintura-40xh4?file=/src/App.js).
305
+ function MyDropzone() {
306
+ const {getRootProps, getInputProps} = useDropzone({noClick: true})
303
307
 
304
- ## Supported Browsers
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); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
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] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
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; // Fn for opening the file dialog programmatically
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, onFileDialogCancel]); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
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
- var acceptedFiles = [];
650
- var fileRejections = [];
651
- files.forEach(function (file) {
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
- if (!multiple && acceptedFiles.length > 1 || multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles) {
683
- // Reject everything and empty accepted files
684
- acceptedFiles.forEach(function (file) {
685
- fileRejections.push({
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
- dispatch({
694
- acceptedFiles: acceptedFiles,
695
- fileRejections: fileRejections,
696
- type: 'setFiles'
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
- if (onDrop) {
700
- onDrop(acceptedFiles, fileRejections, event);
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
- if (fileRejections.length > 0 && onDropRejected) {
704
- onDropRejected(fileRejections, event);
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
- if (acceptedFiles.length > 0 && onDropAccepted) {
708
- onDropAccepted(acceptedFiles, event);
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: 'reset'
734
+ type: 'focus'
715
735
  });
716
- }, [multiple, accept, minSize, maxSize, maxFiles, getFilesFromEvent, onDrop, onDropAccepted, onDropRejected, noDragEventsBubbling, validator]);
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({}, state), {}, {
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';