react-dropzone 13.0.0 → 14.1.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/package.json CHANGED
@@ -95,7 +95,7 @@
95
95
  ],
96
96
  "license": "MIT",
97
97
  "peerDependencies": {
98
- "react": ">= 16.8"
98
+ "react": ">= 16.8 || 18.0.0"
99
99
  },
100
100
  "dependencies": {
101
101
  "attr-accept": "^2.2.2",
@@ -129,8 +129,8 @@
129
129
  "@size-limit/webpack-why": "^7.0.5",
130
130
  "@testing-library/dom": "^8.11.3",
131
131
  "@testing-library/jest-dom": "^5.16.1",
132
- "@testing-library/react": "^12.1.2",
133
- "@testing-library/react-hooks": "^7.0.2",
132
+ "@testing-library/react": "^13.1.1",
133
+ "@testing-library/react-hooks": "^8.0.0",
134
134
  "@types/react": "^17.0.0",
135
135
  "@types/react-dom": "^17.0.0",
136
136
  "@typescript-eslint/eslint-plugin": "^5.10.1",
@@ -156,10 +156,10 @@
156
156
  "markdownlint-cli": "^0.30.0",
157
157
  "pinst": "^2.1.6",
158
158
  "prettier": "^2.5.1",
159
- "react": "^17.0.0",
160
- "react-dom": "^17.0.0",
159
+ "react": "^18.1.0",
160
+ "react-dom": "^18.1.0",
161
161
  "react-styleguidist": "^11.2.0",
162
- "react-test-renderer": "^17.0.0",
162
+ "react-test-renderer": "^18.1.0",
163
163
  "rimraf": "^3.0.2",
164
164
  "rollup": "^2.66.1",
165
165
  "rollup-plugin-terser": "^7.0.2",
@@ -171,7 +171,7 @@
171
171
  "webpack-blocks": "^2.1.0"
172
172
  },
173
173
  "typings": "typings/react-dropzone.d.ts",
174
- "version": "13.0.0",
174
+ "version": "14.1.0",
175
175
  "engines": {
176
176
  "node": ">= 10.13"
177
177
  },
@@ -1,3 +1,3 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`useDropzone() hook behavior renders the root and input nodes with the necessary props 1`] = `"<div role=\\"button\\" tabindex=\\"0\\"><input multiple=\\"\\" type=\\"file\\" style=\\"display: none;\\" tabindex=\\"-1\\"></div>"`;
3
+ exports[`useDropzone() hook behavior renders the root and input nodes with the necessary props 1`] = `"<div role=\\"presentation\\" tabindex=\\"0\\"><input multiple=\\"\\" type=\\"file\\" style=\\"display: none;\\" tabindex=\\"-1\\"></div>"`;
package/src/index.js CHANGED
@@ -68,6 +68,7 @@ const defaultProps = {
68
68
  noDragEventsBubbling: false,
69
69
  validator: null,
70
70
  useFsAccessApi: true,
71
+ autoFocus: false,
71
72
  };
72
73
 
73
74
  Dropzone.defaultProps = defaultProps;
@@ -85,7 +86,6 @@ Dropzone.propTypes = {
85
86
  * @param {boolean} params.isDragActive Active drag is in progress
86
87
  * @param {boolean} params.isDragAccept Dragged files are accepted
87
88
  * @param {boolean} params.isDragReject Some dragged files are rejected
88
- * @param {File[]} params.draggedFiles Files in active drag
89
89
  * @param {File[]} params.acceptedFiles Accepted files
90
90
  * @param {FileRejection[]} params.fileRejections Rejected files and why they were rejected
91
91
  */
@@ -174,6 +174,11 @@ Dropzone.propTypes = {
174
174
  */
175
175
  useFsAccessApi: PropTypes.bool,
176
176
 
177
+ /**
178
+ * Set to true to focus the root element on render
179
+ */
180
+ autoFocus: PropTypes.bool,
181
+
177
182
  /**
178
183
  * Cb for when the `dragenter` event occurs.
179
184
  *
@@ -310,29 +315,33 @@ export default Dropzone;
310
315
  */
311
316
 
312
317
  /**
313
- * An object with the current dropzone state and some helper functions.
318
+ * An object with the current dropzone state.
314
319
  *
315
320
  * @typedef {object} DropzoneState
316
- * @property {Function} getRootProps Returns the props you should apply to the root drop container you render
317
- * @property {Function} getInputProps Returns the props you should apply to hidden file input you render
318
- * @property {Function} open Open the native file selection dialog
319
321
  * @property {boolean} isFocused Dropzone area is in focus
320
322
  * @property {boolean} isFileDialogActive File dialog is opened
321
323
  * @property {boolean} isDragActive Active drag is in progress
322
324
  * @property {boolean} isDragAccept Dragged files are accepted
323
325
  * @property {boolean} isDragReject Some dragged files are rejected
324
- * @property {File[]} draggedFiles Files in active drag
325
326
  * @property {File[]} acceptedFiles Accepted files
326
327
  * @property {FileRejection[]} fileRejections Rejected files and why they were rejected
327
328
  */
328
329
 
330
+ /**
331
+ * An object with the dropzone methods.
332
+ *
333
+ * @typedef {object} DropzoneMethods
334
+ * @property {Function} getRootProps Returns the props you should apply to the root drop container you render
335
+ * @property {Function} getInputProps Returns the props you should apply to hidden file input you render
336
+ * @property {Function} open Open the native file selection dialog
337
+ */
338
+
329
339
  const initialState = {
330
340
  isFocused: false,
331
341
  isFileDialogActive: false,
332
342
  isDragActive: false,
333
343
  isDragAccept: false,
334
344
  isDragReject: false,
335
- draggedFiles: [],
336
345
  acceptedFiles: [],
337
346
  fileRejections: [],
338
347
  };
@@ -378,6 +387,7 @@ const initialState = {
378
387
  * @param {Function} [props.onFileDialogCancel] Cb for when closing the file dialog with no selection
379
388
  * @param {boolean} [props.useFsAccessApi] Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
380
389
  * to open the file picker instead of using an `<input type="file">` click event.
390
+ * @param {boolean} autoFocus Set to true to auto focus the root element.
381
391
  * @param {Function} [props.onFileDialogOpen] Cb for when opening the file dialog
382
392
  * @param {dragCb} [props.onDragEnter] Cb for when the `dragenter` event occurs.
383
393
  * @param {dragCb} [props.onDragLeave] Cb for when the `dragleave` event occurs
@@ -410,7 +420,7 @@ const initialState = {
410
420
  * @param {dropRejectedCb} [props.onDropRejected]
411
421
  * @param {(error: Error) => void} [props.onError]
412
422
  *
413
- * @returns {DropzoneState}
423
+ * @returns {DropzoneState & DropzoneMethods}
414
424
  */
415
425
  export function useDropzone(props = {}) {
416
426
  const {
@@ -430,6 +440,7 @@ export function useDropzone(props = {}) {
430
440
  onFileDialogCancel,
431
441
  onFileDialogOpen,
432
442
  useFsAccessApi,
443
+ autoFocus,
433
444
  preventDropOnDocument,
434
445
  noClick,
435
446
  noKeyboard,
@@ -455,11 +466,16 @@ export function useDropzone(props = {}) {
455
466
  [onFileDialogCancel]
456
467
  );
457
468
 
469
+ /**
470
+ * @constant
471
+ * @type {React.MutableRefObject<HTMLElement>}
472
+ */
458
473
  const rootRef = useRef(null);
474
+
459
475
  const inputRef = useRef(null);
460
476
 
461
477
  const [state, dispatch] = useReducer(reducer, initialState);
462
- const { isFocused, isFileDialogActive, draggedFiles } = state;
478
+ const { isFocused, isFileDialogActive } = state;
463
479
 
464
480
  const fsAccessApiWorksRef = useRef(
465
481
  typeof window !== "undefined" &&
@@ -515,6 +531,14 @@ export function useDropzone(props = {}) {
515
531
  };
516
532
  }, [rootRef, preventDropOnDocument]);
517
533
 
534
+ // Auto focus the root when autoFocus is true
535
+ useEffect(() => {
536
+ if (!disabled && autoFocus && rootRef.current) {
537
+ rootRef.current.focus();
538
+ }
539
+ return () => {};
540
+ }, [rootRef, autoFocus, disabled]);
541
+
518
542
  const onErrCb = useCallback(
519
543
  (e) => {
520
544
  if (onError) {
@@ -538,13 +562,27 @@ export function useDropzone(props = {}) {
538
562
 
539
563
  if (isEvtWithFiles(event)) {
540
564
  Promise.resolve(getFilesFromEvent(event))
541
- .then((draggedFiles) => {
565
+ .then((files) => {
542
566
  if (isPropagationStopped(event) && !noDragEventsBubbling) {
543
567
  return;
544
568
  }
545
569
 
570
+ const fileCount = files.length;
571
+ const isDragAccept =
572
+ fileCount > 0 &&
573
+ allFilesAccepted({
574
+ files,
575
+ accept: acceptAttr,
576
+ minSize,
577
+ maxSize,
578
+ multiple,
579
+ maxFiles,
580
+ });
581
+ const isDragReject = fileCount > 0 && !isDragAccept;
582
+
546
583
  dispatch({
547
- draggedFiles,
584
+ isDragAccept,
585
+ isDragReject,
548
586
  isDragActive: true,
549
587
  type: "setDraggedFiles",
550
588
  });
@@ -556,7 +594,17 @@ export function useDropzone(props = {}) {
556
594
  .catch((e) => onErrCb(e));
557
595
  }
558
596
  },
559
- [getFilesFromEvent, onDragEnter, onErrCb, noDragEventsBubbling]
597
+ [
598
+ getFilesFromEvent,
599
+ onDragEnter,
600
+ onErrCb,
601
+ noDragEventsBubbling,
602
+ acceptAttr,
603
+ minSize,
604
+ maxSize,
605
+ multiple,
606
+ maxFiles,
607
+ ]
560
608
  );
561
609
 
562
610
  const onDragOverCb = useCallback(
@@ -603,9 +651,10 @@ export function useDropzone(props = {}) {
603
651
  }
604
652
 
605
653
  dispatch({
606
- isDragActive: false,
607
654
  type: "setDraggedFiles",
608
- draggedFiles: [],
655
+ isDragActive: false,
656
+ isDragAccept: false,
657
+ isDragReject: false,
609
658
  });
610
659
 
611
660
  if (isEvtWithFiles(event) && onDragLeave) {
@@ -857,7 +906,7 @@ export function useDropzone(props = {}) {
857
906
  composeEventHandlers(onDragLeave, onDragLeaveCb)
858
907
  ),
859
908
  onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)),
860
- role: typeof role === "string" && role !== "" ? role : "button",
909
+ role: typeof role === "string" && role !== "" ? role : "presentation",
861
910
  [refKey]: rootRef,
862
911
  ...(!disabled && !noKeyboard ? { tabIndex: 0 } : {}),
863
912
  ...rest,
@@ -906,23 +955,8 @@ export function useDropzone(props = {}) {
906
955
  [inputRef, accept, multiple, onDropCb, disabled]
907
956
  );
908
957
 
909
- const fileCount = draggedFiles.length;
910
- const isDragAccept =
911
- fileCount > 0 &&
912
- allFilesAccepted({
913
- files: draggedFiles,
914
- accept: acceptAttr,
915
- minSize,
916
- maxSize,
917
- multiple,
918
- maxFiles,
919
- });
920
- const isDragReject = fileCount > 0 && !isDragAccept;
921
-
922
958
  return {
923
959
  ...state,
924
- isDragAccept,
925
- isDragReject,
926
960
  isFocused: isFocused && !disabled,
927
961
  getRootProps,
928
962
  getInputProps,
@@ -932,6 +966,11 @@ export function useDropzone(props = {}) {
932
966
  };
933
967
  }
934
968
 
969
+ /**
970
+ * @param {DropzoneState} state
971
+ * @param {{type: string} & DropzoneState} action
972
+ * @returns {DropzoneState}
973
+ */
935
974
  function reducer(state, action) {
936
975
  /* istanbul ignore next */
937
976
  switch (action.type) {
@@ -956,12 +995,11 @@ function reducer(state, action) {
956
995
  isFileDialogActive: false,
957
996
  };
958
997
  case "setDraggedFiles":
959
- /* eslint no-case-declarations: 0 */
960
- const { isDragActive, draggedFiles } = action;
961
998
  return {
962
999
  ...state,
963
- draggedFiles,
964
- isDragActive,
1000
+ isDragActive: action.isDragActive,
1001
+ isDragAccept: action.isDragAccept,
1002
+ isDragReject: action.isDragReject,
965
1003
  };
966
1004
  case "setFiles":
967
1005
  return {