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