react-dropzone 11.6.0 → 12.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +1 -3
- package/README.md +2 -1
- package/commitlint.config.js +1 -1
- package/dist/es/index.js +56 -44
- package/dist/es/utils/index.js +22 -22
- package/dist/index.js +2 -2
- package/examples/no-jsx/README.md +32 -0
- package/package.json +5 -4
- package/rollup.config.js +20 -20
- package/src/.eslintrc +1 -2
- package/src/__snapshots__/index.spec.js.snap +1 -1
- package/src/index.js +320 -275
- package/src/index.spec.js +1740 -1484
- package/src/utils/index.js +100 -73
- package/src/utils/index.spec.js +386 -289
- package/styleguide.config.js +57 -50
- package/testSetup.js +1 -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,339 +457,356 @@ 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 (canUseFileSystemAccessAPI()) {
|
|
456
|
-
return () => {}
|
|
471
|
+
if (useFsAccessApi && canUseFileSystemAccessAPI()) {
|
|
472
|
+
return () => {};
|
|
457
473
|
}
|
|
458
474
|
|
|
459
|
-
window.addEventListener(
|
|
475
|
+
window.addEventListener("focus", onWindowFocus, false);
|
|
460
476
|
return () => {
|
|
461
|
-
window.removeEventListener(
|
|
462
|
-
}
|
|
463
|
-
}, [inputRef, isFileDialogActive, onFileDialogCancelCb])
|
|
477
|
+
window.removeEventListener("focus", onWindowFocus, false);
|
|
478
|
+
};
|
|
479
|
+
}, [inputRef, isFileDialogActive, onFileDialogCancelCb, useFsAccessApi]);
|
|
464
480
|
|
|
465
|
-
const dragTargetsRef = useRef([])
|
|
466
|
-
const onDocumentDrop = event => {
|
|
481
|
+
const dragTargetsRef = useRef([]);
|
|
482
|
+
const onDocumentDrop = (event) => {
|
|
467
483
|
if (rootRef.current && rootRef.current.contains(event.target)) {
|
|
468
484
|
// If we intercepted an event for our instance, let it propagate down to the instance's onDrop handler
|
|
469
|
-
return
|
|
485
|
+
return;
|
|
470
486
|
}
|
|
471
|
-
event.preventDefault()
|
|
472
|
-
dragTargetsRef.current = []
|
|
473
|
-
}
|
|
487
|
+
event.preventDefault();
|
|
488
|
+
dragTargetsRef.current = [];
|
|
489
|
+
};
|
|
474
490
|
|
|
475
491
|
useEffect(() => {
|
|
476
492
|
if (preventDropOnDocument) {
|
|
477
|
-
document.addEventListener(
|
|
478
|
-
document.addEventListener(
|
|
493
|
+
document.addEventListener("dragover", onDocumentDragOver, false);
|
|
494
|
+
document.addEventListener("drop", onDocumentDrop, false);
|
|
479
495
|
}
|
|
480
496
|
|
|
481
497
|
return () => {
|
|
482
498
|
if (preventDropOnDocument) {
|
|
483
|
-
document.removeEventListener(
|
|
484
|
-
document.removeEventListener(
|
|
499
|
+
document.removeEventListener("dragover", onDocumentDragOver);
|
|
500
|
+
document.removeEventListener("drop", onDocumentDrop);
|
|
485
501
|
}
|
|
486
|
-
}
|
|
487
|
-
}, [rootRef, preventDropOnDocument])
|
|
502
|
+
};
|
|
503
|
+
}, [rootRef, preventDropOnDocument]);
|
|
488
504
|
|
|
489
505
|
const onDragEnterCb = useCallback(
|
|
490
|
-
event => {
|
|
491
|
-
event.preventDefault()
|
|
506
|
+
(event) => {
|
|
507
|
+
event.preventDefault();
|
|
492
508
|
// Persist here because we need the event later after getFilesFromEvent() is done
|
|
493
|
-
event.persist()
|
|
494
|
-
stopPropagation(event)
|
|
509
|
+
event.persist();
|
|
510
|
+
stopPropagation(event);
|
|
495
511
|
|
|
496
|
-
dragTargetsRef.current = [...dragTargetsRef.current, event.target]
|
|
512
|
+
dragTargetsRef.current = [...dragTargetsRef.current, event.target];
|
|
497
513
|
|
|
498
514
|
if (isEvtWithFiles(event)) {
|
|
499
|
-
Promise.resolve(getFilesFromEvent(event)).then(draggedFiles => {
|
|
515
|
+
Promise.resolve(getFilesFromEvent(event)).then((draggedFiles) => {
|
|
500
516
|
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
|
501
|
-
return
|
|
517
|
+
return;
|
|
502
518
|
}
|
|
503
519
|
|
|
504
520
|
dispatch({
|
|
505
521
|
draggedFiles,
|
|
506
522
|
isDragActive: true,
|
|
507
|
-
type:
|
|
508
|
-
})
|
|
523
|
+
type: "setDraggedFiles",
|
|
524
|
+
});
|
|
509
525
|
|
|
510
526
|
if (onDragEnter) {
|
|
511
|
-
onDragEnter(event)
|
|
527
|
+
onDragEnter(event);
|
|
512
528
|
}
|
|
513
|
-
})
|
|
529
|
+
});
|
|
514
530
|
}
|
|
515
531
|
},
|
|
516
532
|
[getFilesFromEvent, onDragEnter, noDragEventsBubbling]
|
|
517
|
-
)
|
|
533
|
+
);
|
|
518
534
|
|
|
519
535
|
const onDragOverCb = useCallback(
|
|
520
|
-
event => {
|
|
521
|
-
event.preventDefault()
|
|
522
|
-
event.persist()
|
|
523
|
-
stopPropagation(event)
|
|
536
|
+
(event) => {
|
|
537
|
+
event.preventDefault();
|
|
538
|
+
event.persist();
|
|
539
|
+
stopPropagation(event);
|
|
524
540
|
|
|
525
541
|
const hasFiles = isEvtWithFiles(event);
|
|
526
542
|
if (hasFiles && event.dataTransfer) {
|
|
527
543
|
try {
|
|
528
|
-
event.dataTransfer.dropEffect =
|
|
544
|
+
event.dataTransfer.dropEffect = "copy";
|
|
529
545
|
} catch {} /* eslint-disable-line no-empty */
|
|
530
546
|
}
|
|
531
547
|
|
|
532
548
|
if (hasFiles && onDragOver) {
|
|
533
|
-
onDragOver(event)
|
|
549
|
+
onDragOver(event);
|
|
534
550
|
}
|
|
535
551
|
|
|
536
|
-
return false
|
|
552
|
+
return false;
|
|
537
553
|
},
|
|
538
554
|
[onDragOver, noDragEventsBubbling]
|
|
539
|
-
)
|
|
555
|
+
);
|
|
540
556
|
|
|
541
557
|
const onDragLeaveCb = useCallback(
|
|
542
|
-
event => {
|
|
543
|
-
event.preventDefault()
|
|
544
|
-
event.persist()
|
|
545
|
-
stopPropagation(event)
|
|
558
|
+
(event) => {
|
|
559
|
+
event.preventDefault();
|
|
560
|
+
event.persist();
|
|
561
|
+
stopPropagation(event);
|
|
546
562
|
|
|
547
563
|
// Only deactivate once the dropzone and all children have been left
|
|
548
564
|
const targets = dragTargetsRef.current.filter(
|
|
549
|
-
target => rootRef.current && rootRef.current.contains(target)
|
|
550
|
-
)
|
|
565
|
+
(target) => rootRef.current && rootRef.current.contains(target)
|
|
566
|
+
);
|
|
551
567
|
// Make sure to remove a target present multiple times only once
|
|
552
568
|
// (Firefox may fire dragenter/dragleave multiple times on the same element)
|
|
553
|
-
const targetIdx = targets.indexOf(event.target)
|
|
569
|
+
const targetIdx = targets.indexOf(event.target);
|
|
554
570
|
if (targetIdx !== -1) {
|
|
555
|
-
targets.splice(targetIdx, 1)
|
|
571
|
+
targets.splice(targetIdx, 1);
|
|
556
572
|
}
|
|
557
|
-
dragTargetsRef.current = targets
|
|
573
|
+
dragTargetsRef.current = targets;
|
|
558
574
|
if (targets.length > 0) {
|
|
559
|
-
return
|
|
575
|
+
return;
|
|
560
576
|
}
|
|
561
577
|
|
|
562
578
|
dispatch({
|
|
563
579
|
isDragActive: false,
|
|
564
|
-
type:
|
|
565
|
-
draggedFiles: []
|
|
566
|
-
})
|
|
580
|
+
type: "setDraggedFiles",
|
|
581
|
+
draggedFiles: [],
|
|
582
|
+
});
|
|
567
583
|
|
|
568
584
|
if (isEvtWithFiles(event) && onDragLeave) {
|
|
569
|
-
onDragLeave(event)
|
|
585
|
+
onDragLeave(event);
|
|
570
586
|
}
|
|
571
587
|
},
|
|
572
588
|
[rootRef, onDragLeave, noDragEventsBubbling]
|
|
573
|
-
)
|
|
589
|
+
);
|
|
574
590
|
|
|
575
|
-
const setFiles = useCallback(
|
|
576
|
-
|
|
577
|
-
|
|
591
|
+
const setFiles = useCallback(
|
|
592
|
+
(files, event) => {
|
|
593
|
+
const acceptedFiles = [];
|
|
594
|
+
const fileRejections = [];
|
|
578
595
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
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;
|
|
583
600
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
601
|
+
if (accepted && sizeMatch && !customErrors) {
|
|
602
|
+
acceptedFiles.push(file);
|
|
603
|
+
} else {
|
|
604
|
+
let errors = [acceptError, sizeError];
|
|
588
605
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
606
|
+
if (customErrors) {
|
|
607
|
+
errors = errors.concat(customErrors);
|
|
608
|
+
}
|
|
592
609
|
|
|
593
|
-
|
|
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);
|
|
594
623
|
}
|
|
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
624
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
625
|
+
dispatch({
|
|
626
|
+
acceptedFiles,
|
|
627
|
+
fileRejections,
|
|
628
|
+
type: "setFiles",
|
|
629
|
+
});
|
|
610
630
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
631
|
+
if (onDrop) {
|
|
632
|
+
onDrop(acceptedFiles, fileRejections, event);
|
|
633
|
+
}
|
|
614
634
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
635
|
+
if (fileRejections.length > 0 && onDropRejected) {
|
|
636
|
+
onDropRejected(fileRejections, event);
|
|
637
|
+
}
|
|
618
638
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
+
);
|
|
634
656
|
|
|
635
657
|
const onDropCb = useCallback(
|
|
636
|
-
event => {
|
|
637
|
-
event.preventDefault()
|
|
658
|
+
(event) => {
|
|
659
|
+
event.preventDefault();
|
|
638
660
|
// Persist here because we need the event later after getFilesFromEvent() is done
|
|
639
|
-
event.persist()
|
|
640
|
-
stopPropagation(event)
|
|
661
|
+
event.persist();
|
|
662
|
+
stopPropagation(event);
|
|
641
663
|
|
|
642
|
-
dragTargetsRef.current = []
|
|
664
|
+
dragTargetsRef.current = [];
|
|
643
665
|
|
|
644
666
|
if (isEvtWithFiles(event)) {
|
|
645
|
-
Promise.resolve(getFilesFromEvent(event)).then(files => {
|
|
667
|
+
Promise.resolve(getFilesFromEvent(event)).then((files) => {
|
|
646
668
|
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
|
647
|
-
return
|
|
669
|
+
return;
|
|
648
670
|
}
|
|
649
|
-
setFiles(files, event)
|
|
650
|
-
})
|
|
671
|
+
setFiles(files, event);
|
|
672
|
+
});
|
|
651
673
|
}
|
|
652
|
-
dispatch({type:
|
|
674
|
+
dispatch({ type: "reset" });
|
|
653
675
|
},
|
|
654
|
-
[
|
|
655
|
-
|
|
656
|
-
setFiles,
|
|
657
|
-
noDragEventsBubbling
|
|
658
|
-
]
|
|
659
|
-
)
|
|
676
|
+
[getFilesFromEvent, setFiles, noDragEventsBubbling]
|
|
677
|
+
);
|
|
660
678
|
|
|
661
679
|
// Fn for opening the file dialog programmatically
|
|
662
680
|
const openFileDialog = useCallback(() => {
|
|
663
|
-
if (canUseFileSystemAccessAPI()) {
|
|
664
|
-
dispatch({type:
|
|
665
|
-
onFileDialogOpenCb()
|
|
681
|
+
if (useFsAccessApi && canUseFileSystemAccessAPI()) {
|
|
682
|
+
dispatch({ type: "openDialog" });
|
|
683
|
+
onFileDialogOpenCb();
|
|
666
684
|
// https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
|
667
685
|
const opts = {
|
|
668
686
|
multiple,
|
|
669
|
-
types: filePickerOptionsTypes(accept)
|
|
687
|
+
types: filePickerOptionsTypes(accept),
|
|
670
688
|
};
|
|
671
|
-
window
|
|
672
|
-
.
|
|
673
|
-
.then(
|
|
674
|
-
.
|
|
675
|
-
.
|
|
676
|
-
|
|
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;
|
|
677
696
|
}
|
|
678
697
|
|
|
679
698
|
if (inputRef.current) {
|
|
680
|
-
dispatch({type:
|
|
681
|
-
onFileDialogOpenCb()
|
|
682
|
-
inputRef.current.value = null
|
|
683
|
-
inputRef.current.click()
|
|
699
|
+
dispatch({ type: "openDialog" });
|
|
700
|
+
onFileDialogOpenCb();
|
|
701
|
+
inputRef.current.value = null;
|
|
702
|
+
inputRef.current.click();
|
|
684
703
|
}
|
|
685
704
|
}, [
|
|
686
705
|
dispatch,
|
|
687
706
|
onFileDialogOpenCb,
|
|
688
707
|
onFileDialogCancelCb,
|
|
708
|
+
useFsAccessApi,
|
|
689
709
|
setFiles,
|
|
690
710
|
accept,
|
|
691
|
-
multiple
|
|
692
|
-
])
|
|
711
|
+
multiple,
|
|
712
|
+
]);
|
|
693
713
|
|
|
694
714
|
// Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
|
|
695
715
|
const onKeyDownCb = useCallback(
|
|
696
|
-
event => {
|
|
716
|
+
(event) => {
|
|
697
717
|
// Ignore keyboard events bubbling up the DOM tree
|
|
698
718
|
if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) {
|
|
699
|
-
return
|
|
719
|
+
return;
|
|
700
720
|
}
|
|
701
721
|
|
|
702
722
|
if (event.keyCode === 32 || event.keyCode === 13) {
|
|
703
|
-
event.preventDefault()
|
|
704
|
-
openFileDialog()
|
|
723
|
+
event.preventDefault();
|
|
724
|
+
openFileDialog();
|
|
705
725
|
}
|
|
706
726
|
},
|
|
707
727
|
[rootRef, inputRef, openFileDialog]
|
|
708
|
-
)
|
|
728
|
+
);
|
|
709
729
|
|
|
710
730
|
// Update focus state for the dropzone
|
|
711
731
|
const onFocusCb = useCallback(() => {
|
|
712
|
-
dispatch({type:
|
|
713
|
-
}, [])
|
|
732
|
+
dispatch({ type: "focus" });
|
|
733
|
+
}, []);
|
|
714
734
|
const onBlurCb = useCallback(() => {
|
|
715
|
-
dispatch({type:
|
|
716
|
-
}, [])
|
|
735
|
+
dispatch({ type: "blur" });
|
|
736
|
+
}, []);
|
|
717
737
|
|
|
718
738
|
// Cb to open the file dialog when click occurs on the dropzone
|
|
719
739
|
const onClickCb = useCallback(() => {
|
|
720
740
|
if (noClick) {
|
|
721
|
-
return
|
|
741
|
+
return;
|
|
722
742
|
}
|
|
723
743
|
|
|
724
744
|
// In IE11/Edge the file-browser dialog is blocking, therefore, use setTimeout()
|
|
725
745
|
// to ensure React can handle state changes
|
|
726
746
|
// See: https://github.com/react-dropzone/react-dropzone/issues/450
|
|
727
747
|
if (isIeOrEdge()) {
|
|
728
|
-
setTimeout(openFileDialog, 0)
|
|
748
|
+
setTimeout(openFileDialog, 0);
|
|
729
749
|
} else {
|
|
730
|
-
openFileDialog()
|
|
750
|
+
openFileDialog();
|
|
731
751
|
}
|
|
732
|
-
}, [inputRef, noClick, openFileDialog])
|
|
752
|
+
}, [inputRef, noClick, openFileDialog]);
|
|
733
753
|
|
|
734
|
-
const composeHandler = fn => {
|
|
735
|
-
return disabled ? null : fn
|
|
736
|
-
}
|
|
754
|
+
const composeHandler = (fn) => {
|
|
755
|
+
return disabled ? null : fn;
|
|
756
|
+
};
|
|
737
757
|
|
|
738
|
-
const composeKeyboardHandler = fn => {
|
|
739
|
-
return noKeyboard ? null : composeHandler(fn)
|
|
740
|
-
}
|
|
758
|
+
const composeKeyboardHandler = (fn) => {
|
|
759
|
+
return noKeyboard ? null : composeHandler(fn);
|
|
760
|
+
};
|
|
741
761
|
|
|
742
|
-
const composeDragHandler = fn => {
|
|
743
|
-
return noDrag ? null : composeHandler(fn)
|
|
744
|
-
}
|
|
762
|
+
const composeDragHandler = (fn) => {
|
|
763
|
+
return noDrag ? null : composeHandler(fn);
|
|
764
|
+
};
|
|
745
765
|
|
|
746
|
-
const stopPropagation = event => {
|
|
766
|
+
const stopPropagation = (event) => {
|
|
747
767
|
if (noDragEventsBubbling) {
|
|
748
|
-
event.stopPropagation()
|
|
768
|
+
event.stopPropagation();
|
|
749
769
|
}
|
|
750
|
-
}
|
|
770
|
+
};
|
|
751
771
|
|
|
752
772
|
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
|
-
|
|
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
|
+
}),
|
|
777
810
|
[
|
|
778
811
|
rootRef,
|
|
779
812
|
onKeyDownCb,
|
|
@@ -786,39 +819,51 @@ export function useDropzone(options = {}) {
|
|
|
786
819
|
onDropCb,
|
|
787
820
|
noKeyboard,
|
|
788
821
|
noDrag,
|
|
789
|
-
disabled
|
|
822
|
+
disabled,
|
|
790
823
|
]
|
|
791
|
-
)
|
|
824
|
+
);
|
|
792
825
|
|
|
793
|
-
const onInputElementClick = useCallback(event => {
|
|
794
|
-
event.stopPropagation()
|
|
795
|
-
}, [])
|
|
826
|
+
const onInputElementClick = useCallback((event) => {
|
|
827
|
+
event.stopPropagation();
|
|
828
|
+
}, []);
|
|
796
829
|
|
|
797
830
|
const getInputProps = useMemo(
|
|
798
|
-
() =>
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
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
|
+
},
|
|
816
852
|
[inputRef, accept, multiple, onDropCb, disabled]
|
|
817
|
-
)
|
|
818
|
-
|
|
819
|
-
const fileCount = draggedFiles.length
|
|
820
|
-
const isDragAccept =
|
|
821
|
-
|
|
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;
|
|
822
867
|
|
|
823
868
|
return {
|
|
824
869
|
...state,
|
|
@@ -829,56 +874,56 @@ export function useDropzone(options = {}) {
|
|
|
829
874
|
getInputProps,
|
|
830
875
|
rootRef,
|
|
831
876
|
inputRef,
|
|
832
|
-
open: composeHandler(openFileDialog)
|
|
833
|
-
}
|
|
877
|
+
open: composeHandler(openFileDialog),
|
|
878
|
+
};
|
|
834
879
|
}
|
|
835
880
|
|
|
836
881
|
function reducer(state, action) {
|
|
837
882
|
/* istanbul ignore next */
|
|
838
883
|
switch (action.type) {
|
|
839
|
-
case
|
|
884
|
+
case "focus":
|
|
840
885
|
return {
|
|
841
886
|
...state,
|
|
842
|
-
isFocused: true
|
|
843
|
-
}
|
|
844
|
-
case
|
|
887
|
+
isFocused: true,
|
|
888
|
+
};
|
|
889
|
+
case "blur":
|
|
845
890
|
return {
|
|
846
891
|
...state,
|
|
847
|
-
isFocused: false
|
|
848
|
-
}
|
|
849
|
-
case
|
|
892
|
+
isFocused: false,
|
|
893
|
+
};
|
|
894
|
+
case "openDialog":
|
|
850
895
|
return {
|
|
851
896
|
...initialState,
|
|
852
|
-
isFileDialogActive: true
|
|
853
|
-
}
|
|
854
|
-
case
|
|
897
|
+
isFileDialogActive: true,
|
|
898
|
+
};
|
|
899
|
+
case "closeDialog":
|
|
855
900
|
return {
|
|
856
901
|
...state,
|
|
857
|
-
isFileDialogActive: false
|
|
858
|
-
}
|
|
859
|
-
case
|
|
902
|
+
isFileDialogActive: false,
|
|
903
|
+
};
|
|
904
|
+
case "setDraggedFiles":
|
|
860
905
|
/* eslint no-case-declarations: 0 */
|
|
861
|
-
const {isDragActive, draggedFiles} = action
|
|
906
|
+
const { isDragActive, draggedFiles } = action;
|
|
862
907
|
return {
|
|
863
908
|
...state,
|
|
864
909
|
draggedFiles,
|
|
865
|
-
isDragActive
|
|
866
|
-
}
|
|
867
|
-
case
|
|
910
|
+
isDragActive,
|
|
911
|
+
};
|
|
912
|
+
case "setFiles":
|
|
868
913
|
return {
|
|
869
914
|
...state,
|
|
870
915
|
acceptedFiles: action.acceptedFiles,
|
|
871
|
-
fileRejections: action.fileRejections
|
|
872
|
-
}
|
|
873
|
-
case
|
|
916
|
+
fileRejections: action.fileRejections,
|
|
917
|
+
};
|
|
918
|
+
case "reset":
|
|
874
919
|
return {
|
|
875
|
-
...initialState
|
|
876
|
-
}
|
|
920
|
+
...initialState,
|
|
921
|
+
};
|
|
877
922
|
default:
|
|
878
|
-
return state
|
|
923
|
+
return state;
|
|
879
924
|
}
|
|
880
925
|
}
|
|
881
926
|
|
|
882
927
|
function noop() {}
|
|
883
928
|
|
|
884
|
-
export {ErrorCode} from
|
|
929
|
+
export { ErrorCode } from "./utils";
|