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