react-dropzone 11.5.3 → 12.0.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/src/index.js CHANGED
@@ -7,21 +7,23 @@ import React, {
7
7
  useImperativeHandle,
8
8
  useMemo,
9
9
  useReducer,
10
- useRef
11
- } from 'react'
12
- import PropTypes from 'prop-types'
13
- import { fromEvent } from 'file-selector'
10
+ useRef,
11
+ } from "react";
12
+ import PropTypes from "prop-types";
13
+ import { fromEvent } from "file-selector";
14
14
  import {
15
15
  allFilesAccepted,
16
16
  composeEventHandlers,
17
17
  fileAccepted,
18
18
  fileMatchSize,
19
+ filePickerOptionsTypes,
20
+ canUseFileSystemAccessAPI,
19
21
  isEvtWithFiles,
20
22
  isIeOrEdge,
21
23
  isPropagationStopped,
22
24
  onDocumentDragOver,
23
- TOO_MANY_FILES_REJECTION
24
- } from './utils/index'
25
+ TOO_MANY_FILES_REJECTION,
26
+ } from "./utils/index";
25
27
 
26
28
  /**
27
29
  * Convenience wrapper component for the `useDropzone` hook
@@ -38,15 +40,15 @@ import {
38
40
  * ```
39
41
  */
40
42
  const Dropzone = forwardRef(({ children, ...params }, ref) => {
41
- const { open, ...props } = useDropzone(params)
43
+ const { open, ...props } = useDropzone(params);
42
44
 
43
- useImperativeHandle(ref, () => ({ open }), [open])
45
+ useImperativeHandle(ref, () => ({ open }), [open]);
44
46
 
45
47
  // TODO: Figure out why react-styleguidist cannot create docs if we don't return a jsx element
46
- return <Fragment>{children({ ...props, open })}</Fragment>
47
- })
48
+ return <Fragment>{children({ ...props, open })}</Fragment>;
49
+ });
48
50
 
49
- Dropzone.displayName = 'Dropzone'
51
+ Dropzone.displayName = "Dropzone";
50
52
 
51
53
  // Add default props for react-docgen
52
54
  const defaultProps = {
@@ -61,10 +63,11 @@ const defaultProps = {
61
63
  noKeyboard: false,
62
64
  noDrag: false,
63
65
  noDragEventsBubbling: false,
64
- validator: null
65
- }
66
+ validator: null,
67
+ useFsAccessApi: true,
68
+ };
66
69
 
67
- Dropzone.defaultProps = defaultProps
70
+ Dropzone.defaultProps = defaultProps;
68
71
 
69
72
  Dropzone.propTypes = {
70
73
  /**
@@ -93,7 +96,10 @@ Dropzone.propTypes = {
93
96
  * Windows. In some cases there might not be a mime type set at all.
94
97
  * See: https://github.com/react-dropzone/react-dropzone/issues/276
95
98
  */
96
- accept: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
99
+ accept: PropTypes.oneOfType([
100
+ PropTypes.string,
101
+ PropTypes.arrayOf(PropTypes.string),
102
+ ]),
97
103
 
98
104
  /**
99
105
  * Allow drag 'n' drop (or selection from the file dialog) of multiple files
@@ -158,11 +164,17 @@ Dropzone.propTypes = {
158
164
  */
159
165
  onFileDialogCancel: PropTypes.func,
160
166
 
161
- /**
162
- * Cb for when opening the file dialog
163
- */
167
+ /**
168
+ * Cb for when opening the file dialog
169
+ */
164
170
  onFileDialogOpen: PropTypes.func,
165
171
 
172
+ /**
173
+ * Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
174
+ * to open the file picker instead of using an `<input type="file">` click event.
175
+ */
176
+ useFsAccessApi: PropTypes.bool,
177
+
166
178
  /**
167
179
  * Cb for when the `dragenter` event occurs.
168
180
  *
@@ -239,10 +251,10 @@ Dropzone.propTypes = {
239
251
  * @param {File} file
240
252
  * @returns {FileError|FileError[]}
241
253
  */
242
- validator: PropTypes.func
243
- }
254
+ validator: PropTypes.func,
255
+ };
244
256
 
245
- export default Dropzone
257
+ export default Dropzone;
246
258
 
247
259
  /**
248
260
  * A function that is invoked for the `dragenter`,
@@ -316,8 +328,8 @@ const initialState = {
316
328
  isDragReject: false,
317
329
  draggedFiles: [],
318
330
  acceptedFiles: [],
319
- fileRejections: []
320
- }
331
+ fileRejections: [],
332
+ };
321
333
 
322
334
  /**
323
335
  * A React hook that creates a drag 'n' drop area.
@@ -359,6 +371,8 @@ const initialState = {
359
371
  * @param {boolean} [props.disabled=false] Enable/disable the dropzone
360
372
  * @param {getFilesFromEvent} [props.getFilesFromEvent] Use this to provide a custom file aggregator
361
373
  * @param {Function} [props.onFileDialogCancel] Cb for when closing the file dialog with no selection
374
+ * @param {boolean} [props.useFsAccessApi] Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
375
+ * to open the file picker instead of using an `<input type="file">` click event.
362
376
  * @param {Function} [props.onFileDialogOpen] Cb for when opening the file dialog
363
377
  * @param {dragCb} [props.onDragEnter] Cb for when the `dragenter` event occurs.
364
378
  * @param {dragCb} [props.onDragLeave] Cb for when the `dragleave` event occurs
@@ -409,34 +423,33 @@ export function useDropzone(options = {}) {
409
423
  onDropRejected,
410
424
  onFileDialogCancel,
411
425
  onFileDialogOpen,
426
+ useFsAccessApi,
412
427
  preventDropOnDocument,
413
428
  noClick,
414
429
  noKeyboard,
415
430
  noDrag,
416
431
  noDragEventsBubbling,
417
- validator
432
+ validator,
418
433
  } = {
419
434
  ...defaultProps,
420
- ...options
421
- }
435
+ ...options,
436
+ };
422
437
 
423
- const rootRef = useRef(null)
424
- const inputRef = useRef(null)
438
+ const onFileDialogOpenCb = useMemo(
439
+ () => (typeof onFileDialogOpen === "function" ? onFileDialogOpen : noop),
440
+ [onFileDialogOpen]
441
+ );
442
+ const onFileDialogCancelCb = useMemo(
443
+ () =>
444
+ typeof onFileDialogCancel === "function" ? onFileDialogCancel : noop,
445
+ [onFileDialogCancel]
446
+ );
425
447
 
426
- const [state, dispatch] = useReducer(reducer, initialState)
427
- const { isFocused, isFileDialogActive, draggedFiles } = state
448
+ const rootRef = useRef(null);
449
+ const inputRef = useRef(null);
428
450
 
429
- // Fn for opening the file dialog programmatically
430
- const openFileDialog = useCallback(() => {
431
- if (inputRef.current) {
432
- dispatch({ type: 'openDialog' })
433
- if (typeof onFileDialogOpen === 'function') {
434
- onFileDialogOpen()
435
- }
436
- inputRef.current.value = null
437
- inputRef.current.click()
438
- }
439
- }, [dispatch, onFileDialogOpen])
451
+ const [state, dispatch] = useReducer(reducer, initialState);
452
+ const { isFocused, isFileDialogActive, draggedFiles } = state;
440
453
 
441
454
  // Update file dialog active state when the window is focused on
442
455
  const onWindowFocus = () => {
@@ -444,299 +457,356 @@ export function useDropzone(options = {}) {
444
457
  if (isFileDialogActive) {
445
458
  setTimeout(() => {
446
459
  if (inputRef.current) {
447
- const { files } = inputRef.current
460
+ const { files } = inputRef.current;
448
461
 
449
462
  if (!files.length) {
450
- dispatch({ type: 'closeDialog' })
451
-
452
- if (typeof onFileDialogCancel === 'function') {
453
- onFileDialogCancel()
454
- }
463
+ dispatch({ type: "closeDialog" });
464
+ onFileDialogCancelCb();
455
465
  }
456
466
  }
457
- }, 300)
467
+ }, 300);
458
468
  }
459
- }
469
+ };
460
470
  useEffect(() => {
461
- window.addEventListener('focus', onWindowFocus, false)
462
- return () => {
463
- window.removeEventListener('focus', onWindowFocus, false)
464
- }
465
- }, [inputRef, isFileDialogActive, onFileDialogCancel])
466
-
467
- // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
468
- const onKeyDownCb = useCallback(
469
- event => {
470
- // Ignore keyboard events bubbling up the DOM tree
471
- if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) {
472
- return
473
- }
474
-
475
- if (event.keyCode === 32 || event.keyCode === 13) {
476
- event.preventDefault()
477
- openFileDialog()
478
- }
479
- },
480
- [rootRef, inputRef, openFileDialog]
481
- )
482
-
483
- // Update focus state for the dropzone
484
- const onFocusCb = useCallback(() => {
485
- dispatch({ type: 'focus' })
486
- }, [])
487
- const onBlurCb = useCallback(() => {
488
- dispatch({ type: 'blur' })
489
- }, [])
490
-
491
- // Cb to open the file dialog when click occurs on the dropzone
492
- const onClickCb = useCallback(() => {
493
- if (noClick) {
494
- return
471
+ if (useFsAccessApi && canUseFileSystemAccessAPI()) {
472
+ return () => {};
495
473
  }
496
474
 
497
- // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
498
- // to ensure React can handle state changes
499
- // See: https://github.com/react-dropzone/react-dropzone/issues/450
500
- if (isIeOrEdge()) {
501
- setTimeout(openFileDialog, 0)
502
- } else {
503
- openFileDialog()
504
- }
505
- }, [inputRef, noClick, openFileDialog])
475
+ window.addEventListener("focus", onWindowFocus, false);
476
+ return () => {
477
+ window.removeEventListener("focus", onWindowFocus, false);
478
+ };
479
+ }, [inputRef, isFileDialogActive, onFileDialogCancelCb, useFsAccessApi]);
506
480
 
507
- const dragTargetsRef = useRef([])
508
- const onDocumentDrop = event => {
481
+ const dragTargetsRef = useRef([]);
482
+ const onDocumentDrop = (event) => {
509
483
  if (rootRef.current && rootRef.current.contains(event.target)) {
510
484
  // If we intercepted an event for our instance, let it propagate down to the instance's onDrop handler
511
- return
485
+ return;
512
486
  }
513
- event.preventDefault()
514
- dragTargetsRef.current = []
515
- }
487
+ event.preventDefault();
488
+ dragTargetsRef.current = [];
489
+ };
516
490
 
517
491
  useEffect(() => {
518
492
  if (preventDropOnDocument) {
519
- document.addEventListener('dragover', onDocumentDragOver, false)
520
- document.addEventListener('drop', onDocumentDrop, false)
493
+ document.addEventListener("dragover", onDocumentDragOver, false);
494
+ document.addEventListener("drop", onDocumentDrop, false);
521
495
  }
522
496
 
523
497
  return () => {
524
498
  if (preventDropOnDocument) {
525
- document.removeEventListener('dragover', onDocumentDragOver)
526
- document.removeEventListener('drop', onDocumentDrop)
499
+ document.removeEventListener("dragover", onDocumentDragOver);
500
+ document.removeEventListener("drop", onDocumentDrop);
527
501
  }
528
- }
529
- }, [rootRef, preventDropOnDocument])
502
+ };
503
+ }, [rootRef, preventDropOnDocument]);
530
504
 
531
505
  const onDragEnterCb = useCallback(
532
- event => {
533
- event.preventDefault()
506
+ (event) => {
507
+ event.preventDefault();
534
508
  // Persist here because we need the event later after getFilesFromEvent() is done
535
- event.persist()
536
- stopPropagation(event)
509
+ event.persist();
510
+ stopPropagation(event);
537
511
 
538
- dragTargetsRef.current = [...dragTargetsRef.current, event.target]
512
+ dragTargetsRef.current = [...dragTargetsRef.current, event.target];
539
513
 
540
514
  if (isEvtWithFiles(event)) {
541
- Promise.resolve(getFilesFromEvent(event)).then(draggedFiles => {
515
+ Promise.resolve(getFilesFromEvent(event)).then((draggedFiles) => {
542
516
  if (isPropagationStopped(event) && !noDragEventsBubbling) {
543
- return
517
+ return;
544
518
  }
545
519
 
546
520
  dispatch({
547
521
  draggedFiles,
548
522
  isDragActive: true,
549
- type: 'setDraggedFiles'
550
- })
523
+ type: "setDraggedFiles",
524
+ });
551
525
 
552
526
  if (onDragEnter) {
553
- onDragEnter(event)
527
+ onDragEnter(event);
554
528
  }
555
- })
529
+ });
556
530
  }
557
531
  },
558
532
  [getFilesFromEvent, onDragEnter, noDragEventsBubbling]
559
- )
533
+ );
560
534
 
561
535
  const onDragOverCb = useCallback(
562
- event => {
563
- event.preventDefault()
564
- event.persist()
565
- stopPropagation(event)
536
+ (event) => {
537
+ event.preventDefault();
538
+ event.persist();
539
+ stopPropagation(event);
566
540
 
567
541
  const hasFiles = isEvtWithFiles(event);
568
542
  if (hasFiles && event.dataTransfer) {
569
543
  try {
570
- event.dataTransfer.dropEffect = 'copy'
544
+ event.dataTransfer.dropEffect = "copy";
571
545
  } catch {} /* eslint-disable-line no-empty */
572
546
  }
573
547
 
574
548
  if (hasFiles && onDragOver) {
575
- onDragOver(event)
549
+ onDragOver(event);
576
550
  }
577
551
 
578
- return false
552
+ return false;
579
553
  },
580
554
  [onDragOver, noDragEventsBubbling]
581
- )
555
+ );
582
556
 
583
557
  const onDragLeaveCb = useCallback(
584
- event => {
585
- event.preventDefault()
586
- event.persist()
587
- stopPropagation(event)
558
+ (event) => {
559
+ event.preventDefault();
560
+ event.persist();
561
+ stopPropagation(event);
588
562
 
589
563
  // Only deactivate once the dropzone and all children have been left
590
564
  const targets = dragTargetsRef.current.filter(
591
- target => rootRef.current && rootRef.current.contains(target)
592
- )
565
+ (target) => rootRef.current && rootRef.current.contains(target)
566
+ );
593
567
  // Make sure to remove a target present multiple times only once
594
568
  // (Firefox may fire dragenter/dragleave multiple times on the same element)
595
- const targetIdx = targets.indexOf(event.target)
569
+ const targetIdx = targets.indexOf(event.target);
596
570
  if (targetIdx !== -1) {
597
- targets.splice(targetIdx, 1)
571
+ targets.splice(targetIdx, 1);
598
572
  }
599
- dragTargetsRef.current = targets
573
+ dragTargetsRef.current = targets;
600
574
  if (targets.length > 0) {
601
- return
575
+ return;
602
576
  }
603
577
 
604
578
  dispatch({
605
579
  isDragActive: false,
606
- type: 'setDraggedFiles',
607
- draggedFiles: []
608
- })
580
+ type: "setDraggedFiles",
581
+ draggedFiles: [],
582
+ });
609
583
 
610
584
  if (isEvtWithFiles(event) && onDragLeave) {
611
- onDragLeave(event)
585
+ onDragLeave(event);
612
586
  }
613
587
  },
614
588
  [rootRef, onDragLeave, noDragEventsBubbling]
615
- )
589
+ );
616
590
 
617
- const onDropCb = useCallback(
618
- event => {
619
- event.preventDefault()
620
- // Persist here because we need the event later after getFilesFromEvent() is done
621
- event.persist()
622
- stopPropagation(event)
591
+ const setFiles = useCallback(
592
+ (files, event) => {
593
+ const acceptedFiles = [];
594
+ const fileRejections = [];
623
595
 
624
- dragTargetsRef.current = []
596
+ files.forEach((file) => {
597
+ const [accepted, acceptError] = fileAccepted(file, accept);
598
+ const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize);
599
+ const customErrors = validator ? validator(file) : null;
625
600
 
626
- if (isEvtWithFiles(event)) {
627
- Promise.resolve(getFilesFromEvent(event)).then(files => {
628
- if (isPropagationStopped(event) && !noDragEventsBubbling) {
629
- return
630
- }
601
+ if (accepted && sizeMatch && !customErrors) {
602
+ acceptedFiles.push(file);
603
+ } else {
604
+ let errors = [acceptError, sizeError];
631
605
 
632
- const acceptedFiles = []
633
- const fileRejections = []
634
-
635
- files.forEach(file => {
636
- const [accepted, acceptError] = fileAccepted(file, accept)
637
- const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize)
638
- const customErrors = validator ? validator(file) : null;
639
-
640
- if (accepted && sizeMatch && !customErrors) {
641
- acceptedFiles.push(file)
642
- } else {
643
- let errors = [acceptError, sizeError];
644
-
645
- if (customErrors) {
646
- errors = errors.concat(customErrors);
647
- }
648
-
649
- fileRejections.push({ file, errors: errors.filter(e => e) })
650
- }
651
- })
652
-
653
- if ((!multiple && acceptedFiles.length > 1) || (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles)) {
654
- // Reject everything and empty accepted files
655
- acceptedFiles.forEach(file => {
656
- fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] })
657
- })
658
- acceptedFiles.splice(0)
606
+ if (customErrors) {
607
+ errors = errors.concat(customErrors);
659
608
  }
660
609
 
661
- dispatch({
662
- acceptedFiles,
663
- fileRejections,
664
- type: 'setFiles'
665
- })
610
+ fileRejections.push({ file, errors: errors.filter((e) => e) });
611
+ }
612
+ });
613
+
614
+ if (
615
+ (!multiple && acceptedFiles.length > 1) ||
616
+ (multiple && maxFiles >= 1 && acceptedFiles.length > maxFiles)
617
+ ) {
618
+ // Reject everything and empty accepted files
619
+ acceptedFiles.forEach((file) => {
620
+ fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] });
621
+ });
622
+ acceptedFiles.splice(0);
623
+ }
666
624
 
667
- if (onDrop) {
668
- onDrop(acceptedFiles, fileRejections, event)
669
- }
625
+ dispatch({
626
+ acceptedFiles,
627
+ fileRejections,
628
+ type: "setFiles",
629
+ });
670
630
 
671
- if (fileRejections.length > 0 && onDropRejected) {
672
- onDropRejected(fileRejections, event)
673
- }
631
+ if (onDrop) {
632
+ onDrop(acceptedFiles, fileRejections, event);
633
+ }
674
634
 
675
- if (acceptedFiles.length > 0 && onDropAccepted) {
676
- onDropAccepted(acceptedFiles, event)
677
- }
678
- })
635
+ if (fileRejections.length > 0 && onDropRejected) {
636
+ onDropRejected(fileRejections, event);
637
+ }
638
+
639
+ if (acceptedFiles.length > 0 && onDropAccepted) {
640
+ onDropAccepted(acceptedFiles, event);
679
641
  }
680
- dispatch({ type: 'reset' })
681
642
  },
682
643
  [
644
+ dispatch,
683
645
  multiple,
684
646
  accept,
685
647
  minSize,
686
648
  maxSize,
687
649
  maxFiles,
688
- getFilesFromEvent,
689
650
  onDrop,
690
651
  onDropAccepted,
691
652
  onDropRejected,
692
- noDragEventsBubbling,
693
- validator
653
+ validator,
694
654
  ]
695
- )
655
+ );
696
656
 
697
- const composeHandler = fn => {
698
- return disabled ? null : fn
699
- }
657
+ const onDropCb = useCallback(
658
+ (event) => {
659
+ event.preventDefault();
660
+ // Persist here because we need the event later after getFilesFromEvent() is done
661
+ event.persist();
662
+ stopPropagation(event);
700
663
 
701
- const composeKeyboardHandler = fn => {
702
- return noKeyboard ? null : composeHandler(fn)
703
- }
664
+ dragTargetsRef.current = [];
704
665
 
705
- const composeDragHandler = fn => {
706
- return noDrag ? null : composeHandler(fn)
707
- }
666
+ if (isEvtWithFiles(event)) {
667
+ Promise.resolve(getFilesFromEvent(event)).then((files) => {
668
+ if (isPropagationStopped(event) && !noDragEventsBubbling) {
669
+ return;
670
+ }
671
+ setFiles(files, event);
672
+ });
673
+ }
674
+ dispatch({ type: "reset" });
675
+ },
676
+ [getFilesFromEvent, setFiles, noDragEventsBubbling]
677
+ );
678
+
679
+ // Fn for opening the file dialog programmatically
680
+ const openFileDialog = useCallback(() => {
681
+ if (useFsAccessApi && canUseFileSystemAccessAPI()) {
682
+ dispatch({ type: "openDialog" });
683
+ onFileDialogOpenCb();
684
+ // https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
685
+ const opts = {
686
+ multiple,
687
+ types: filePickerOptionsTypes(accept),
688
+ };
689
+ window
690
+ .showOpenFilePicker(opts)
691
+ .then((handles) => getFilesFromEvent(handles))
692
+ .then((files) => setFiles(files, null))
693
+ .catch((e) => onFileDialogCancelCb(e))
694
+ .finally(() => dispatch({ type: "closeDialog" }));
695
+ return;
696
+ }
697
+
698
+ if (inputRef.current) {
699
+ dispatch({ type: "openDialog" });
700
+ onFileDialogOpenCb();
701
+ inputRef.current.value = null;
702
+ inputRef.current.click();
703
+ }
704
+ }, [
705
+ dispatch,
706
+ onFileDialogOpenCb,
707
+ onFileDialogCancelCb,
708
+ useFsAccessApi,
709
+ setFiles,
710
+ accept,
711
+ multiple,
712
+ ]);
713
+
714
+ // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
715
+ const onKeyDownCb = useCallback(
716
+ (event) => {
717
+ // Ignore keyboard events bubbling up the DOM tree
718
+ if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) {
719
+ return;
720
+ }
721
+
722
+ if (event.keyCode === 32 || event.keyCode === 13) {
723
+ event.preventDefault();
724
+ openFileDialog();
725
+ }
726
+ },
727
+ [rootRef, inputRef, openFileDialog]
728
+ );
729
+
730
+ // Update focus state for the dropzone
731
+ const onFocusCb = useCallback(() => {
732
+ dispatch({ type: "focus" });
733
+ }, []);
734
+ const onBlurCb = useCallback(() => {
735
+ dispatch({ type: "blur" });
736
+ }, []);
737
+
738
+ // Cb to open the file dialog when click occurs on the dropzone
739
+ const onClickCb = useCallback(() => {
740
+ if (noClick) {
741
+ return;
742
+ }
743
+
744
+ // In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
745
+ // to ensure React can handle state changes
746
+ // See: https://github.com/react-dropzone/react-dropzone/issues/450
747
+ if (isIeOrEdge()) {
748
+ setTimeout(openFileDialog, 0);
749
+ } else {
750
+ openFileDialog();
751
+ }
752
+ }, [inputRef, noClick, openFileDialog]);
753
+
754
+ const composeHandler = (fn) => {
755
+ return disabled ? null : fn;
756
+ };
757
+
758
+ const composeKeyboardHandler = (fn) => {
759
+ return noKeyboard ? null : composeHandler(fn);
760
+ };
761
+
762
+ const composeDragHandler = (fn) => {
763
+ return noDrag ? null : composeHandler(fn);
764
+ };
708
765
 
709
- const stopPropagation = event => {
766
+ const stopPropagation = (event) => {
710
767
  if (noDragEventsBubbling) {
711
- event.stopPropagation()
768
+ event.stopPropagation();
712
769
  }
713
- }
770
+ };
714
771
 
715
772
  const getRootProps = useMemo(
716
- () => ({
717
- refKey = 'ref',
718
- onKeyDown,
719
- onFocus,
720
- onBlur,
721
- onClick,
722
- onDragEnter,
723
- onDragOver,
724
- onDragLeave,
725
- onDrop,
726
- ...rest
727
- } = {}) => ({
728
- onKeyDown: composeKeyboardHandler(composeEventHandlers(onKeyDown, onKeyDownCb)),
729
- onFocus: composeKeyboardHandler(composeEventHandlers(onFocus, onFocusCb)),
730
- onBlur: composeKeyboardHandler(composeEventHandlers(onBlur, onBlurCb)),
731
- onClick: composeHandler(composeEventHandlers(onClick, onClickCb)),
732
- onDragEnter: composeDragHandler(composeEventHandlers(onDragEnter, onDragEnterCb)),
733
- onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)),
734
- onDragLeave: composeDragHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)),
735
- onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)),
736
- [refKey]: rootRef,
737
- ...(!disabled && !noKeyboard ? { tabIndex: 0 } : {}),
738
- ...rest
739
- }),
773
+ () =>
774
+ ({
775
+ refKey = "ref",
776
+ role,
777
+ onKeyDown,
778
+ onFocus,
779
+ onBlur,
780
+ onClick,
781
+ onDragEnter,
782
+ onDragOver,
783
+ onDragLeave,
784
+ onDrop,
785
+ ...rest
786
+ } = {}) => ({
787
+ onKeyDown: composeKeyboardHandler(
788
+ composeEventHandlers(onKeyDown, onKeyDownCb)
789
+ ),
790
+ onFocus: composeKeyboardHandler(
791
+ composeEventHandlers(onFocus, onFocusCb)
792
+ ),
793
+ onBlur: composeKeyboardHandler(composeEventHandlers(onBlur, onBlurCb)),
794
+ onClick: composeHandler(composeEventHandlers(onClick, onClickCb)),
795
+ onDragEnter: composeDragHandler(
796
+ composeEventHandlers(onDragEnter, onDragEnterCb)
797
+ ),
798
+ onDragOver: composeDragHandler(
799
+ composeEventHandlers(onDragOver, onDragOverCb)
800
+ ),
801
+ onDragLeave: composeDragHandler(
802
+ composeEventHandlers(onDragLeave, onDragLeaveCb)
803
+ ),
804
+ onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)),
805
+ role: typeof role === "string" && role !== "" ? role : "button",
806
+ [refKey]: rootRef,
807
+ ...(!disabled && !noKeyboard ? { tabIndex: 0 } : {}),
808
+ ...rest,
809
+ }),
740
810
  [
741
811
  rootRef,
742
812
  onKeyDownCb,
@@ -749,39 +819,51 @@ export function useDropzone(options = {}) {
749
819
  onDropCb,
750
820
  noKeyboard,
751
821
  noDrag,
752
- disabled
822
+ disabled,
753
823
  ]
754
- )
824
+ );
755
825
 
756
- const onInputElementClick = useCallback(event => {
757
- event.stopPropagation()
758
- }, [])
826
+ const onInputElementClick = useCallback((event) => {
827
+ event.stopPropagation();
828
+ }, []);
759
829
 
760
830
  const getInputProps = useMemo(
761
- () => ({ refKey = 'ref', onChange, onClick, ...rest } = {}) => {
762
- const inputProps = {
763
- accept,
764
- multiple,
765
- type: 'file',
766
- style: { display: 'none' },
767
- onChange: composeHandler(composeEventHandlers(onChange, onDropCb)),
768
- onClick: composeHandler(composeEventHandlers(onClick, onInputElementClick)),
769
- autoComplete: 'off',
770
- tabIndex: -1,
771
- [refKey]: inputRef
772
- }
773
-
774
- return {
775
- ...inputProps,
776
- ...rest
777
- }
778
- },
831
+ () =>
832
+ ({ refKey = "ref", onChange, onClick, ...rest } = {}) => {
833
+ const inputProps = {
834
+ accept,
835
+ multiple,
836
+ type: "file",
837
+ style: { display: "none" },
838
+ onChange: composeHandler(composeEventHandlers(onChange, onDropCb)),
839
+ onClick: composeHandler(
840
+ composeEventHandlers(onClick, onInputElementClick)
841
+ ),
842
+ autoComplete: "off",
843
+ tabIndex: -1,
844
+ [refKey]: inputRef,
845
+ };
846
+
847
+ return {
848
+ ...inputProps,
849
+ ...rest,
850
+ };
851
+ },
779
852
  [inputRef, accept, multiple, onDropCb, disabled]
780
- )
853
+ );
781
854
 
782
- const fileCount = draggedFiles.length
783
- const isDragAccept = fileCount > 0 && allFilesAccepted({ files: draggedFiles, accept, minSize, maxSize, multiple, maxFiles })
784
- const isDragReject = fileCount > 0 && !isDragAccept
855
+ const fileCount = draggedFiles.length;
856
+ const isDragAccept =
857
+ fileCount > 0 &&
858
+ allFilesAccepted({
859
+ files: draggedFiles,
860
+ accept,
861
+ minSize,
862
+ maxSize,
863
+ multiple,
864
+ maxFiles,
865
+ });
866
+ const isDragReject = fileCount > 0 && !isDragAccept;
785
867
 
786
868
  return {
787
869
  ...state,
@@ -792,54 +874,56 @@ export function useDropzone(options = {}) {
792
874
  getInputProps,
793
875
  rootRef,
794
876
  inputRef,
795
- open: composeHandler(openFileDialog)
796
- }
877
+ open: composeHandler(openFileDialog),
878
+ };
797
879
  }
798
880
 
799
881
  function reducer(state, action) {
800
882
  /* istanbul ignore next */
801
883
  switch (action.type) {
802
- case 'focus':
884
+ case "focus":
803
885
  return {
804
886
  ...state,
805
- isFocused: true
806
- }
807
- case 'blur':
887
+ isFocused: true,
888
+ };
889
+ case "blur":
808
890
  return {
809
891
  ...state,
810
- isFocused: false
811
- }
812
- case 'openDialog':
892
+ isFocused: false,
893
+ };
894
+ case "openDialog":
813
895
  return {
814
- ...state,
815
- isFileDialogActive: true
816
- }
817
- case 'closeDialog':
896
+ ...initialState,
897
+ isFileDialogActive: true,
898
+ };
899
+ case "closeDialog":
818
900
  return {
819
901
  ...state,
820
- isFileDialogActive: false
821
- }
822
- case 'setDraggedFiles':
902
+ isFileDialogActive: false,
903
+ };
904
+ case "setDraggedFiles":
823
905
  /* eslint no-case-declarations: 0 */
824
- const { isDragActive, draggedFiles } = action
906
+ const { isDragActive, draggedFiles } = action;
825
907
  return {
826
908
  ...state,
827
909
  draggedFiles,
828
- isDragActive
829
- }
830
- case 'setFiles':
910
+ isDragActive,
911
+ };
912
+ case "setFiles":
831
913
  return {
832
914
  ...state,
833
915
  acceptedFiles: action.acceptedFiles,
834
- fileRejections: action.fileRejections
835
- }
836
- case 'reset':
916
+ fileRejections: action.fileRejections,
917
+ };
918
+ case "reset":
837
919
  return {
838
- ...initialState
839
- }
920
+ ...initialState,
921
+ };
840
922
  default:
841
- return state
923
+ return state;
842
924
  }
843
925
  }
844
926
 
845
- export { ErrorCode } from './utils'
927
+ function noop() {}
928
+
929
+ export { ErrorCode } from "./utils";