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