react-dropzone 12.0.6 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -230,7 +230,7 @@ dropzoneRef.open()
230
230
  ```js static
231
231
  import React from 'react'
232
232
  import Dropzone from 'react-dropzone'
233
- import {act, fireEvent, render, waitFor} from '@testing-library/react'
233
+ import {act, fireEvent, render} from '@testing-library/react'
234
234
 
235
235
  test('invoke onDragEnter when dragenter event occurs', async () => {
236
236
  const file = new File([
@@ -248,25 +248,17 @@ test('invoke onDragEnter when dragenter event occurs', async () => {
248
248
  )}
249
249
  </Dropzone>
250
250
  )
251
- const { container, rerender } = render(ui)
252
- const dropzone = container.querySelector('div')
253
-
254
- dispatchEvt(dropzone, 'dragenter', data)
255
- await flushPromises(rerender, ui)
256
-
251
+ const { container } = render(ui)
252
+
253
+ await act(
254
+ () => fireEvent.dragEnter(
255
+ container.querySelector('div'),
256
+ data,
257
+ )
258
+ );
257
259
  expect(onDragEnter).toHaveBeenCalled()
258
260
  })
259
261
 
260
- async function flushPromises(rerender, ui) {
261
- await act(() => waitFor(() => rerender(ui)))
262
- }
263
-
264
- function dispatchEvt(node, type, data) {
265
- const event = new Event(type, { bubbles: true })
266
- Object.assign(event, data)
267
- fireEvent(node, event)
268
- }
269
-
270
262
  function mockData(files) {
271
263
  return {
272
264
  dataTransfer: {
@@ -343,8 +335,6 @@ As one can imagine, this doesn't really work if there's a lot of files or large
343
335
 
344
336
  Fortunately, there's the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API), which is currently a working draft and some browsers support it (see [browser compatibility](https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker#browser_compatibility)), that provides a reliable way to prompt the user for file selection and capture cancellation.
345
337
 
346
- And this lib makes use of it if available. Though, there's a small catch: using file extensions for the `accept` property is not supported; you must use MIME types as described in [common MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types). Also check [accepting specific file types](https://react-dropzone.js.org/#section-accepting-specific-file-types) for more info on the subject of `accept` limitations.
347
-
348
338
  Also keep in mind that the FS access API can only be used in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
349
339
 
350
340
  **NOTE** You can disable using the FS access API with the `useFsAccessApi` property: `useDropzone({useFsAccessApi: false})`.
package/dist/es/index.js CHANGED
@@ -37,7 +37,7 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
37
37
  import React, { forwardRef, Fragment, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef } from "react";
38
38
  import PropTypes from "prop-types";
39
39
  import { fromEvent } from "file-selector";
40
- import { allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, filePickerOptionsTypes, canUseFileSystemAccessAPI, isAbort, isEvtWithFiles, isIeOrEdge, isPropagationStopped, isSecurityError, onDocumentDragOver, TOO_MANY_FILES_REJECTION } from "./utils/index";
40
+ import { acceptPropAsAcceptAttr, allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, canUseFileSystemAccessAPI, isAbort, isEvtWithFiles, isIeOrEdge, isPropagationStopped, isSecurityError, onDocumentDragOver, pickerOptionsFromAccept, TOO_MANY_FILES_REJECTION } from "./utils/index";
41
41
  /**
42
42
  * Convenience wrapper component for the `useDropzone` hook
43
43
  *
@@ -102,7 +102,6 @@ Dropzone.propTypes = {
102
102
  * @param {boolean} params.isDragActive Active drag is in progress
103
103
  * @param {boolean} params.isDragAccept Dragged files are accepted
104
104
  * @param {boolean} params.isDragReject Some dragged files are rejected
105
- * @param {File[]} params.draggedFiles Files in active drag
106
105
  * @param {File[]} params.acceptedFiles Accepted files
107
106
  * @param {FileRejection[]} params.fileRejections Rejected files and why they were rejected
108
107
  */
@@ -110,13 +109,12 @@ Dropzone.propTypes = {
110
109
 
111
110
  /**
112
111
  * Set accepted file types.
113
- * See https://github.com/okonet/attr-accept for more information.
112
+ * Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
114
113
  * Keep in mind that mime type determination is not reliable across platforms. CSV files,
115
114
  * for example, are reported as text/plain under macOS but as application/vnd.ms-excel under
116
- * Windows. In some cases there might not be a mime type set at all.
117
- * See: https://github.com/react-dropzone/react-dropzone/issues/276
115
+ * Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
118
116
  */
119
- accept: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
117
+ accept: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.string)),
120
118
 
121
119
  /**
122
120
  * Allow drag 'n' drop (or selection from the file dialog) of multiple files
@@ -264,6 +262,13 @@ Dropzone.propTypes = {
264
262
  */
265
263
  onDropRejected: PropTypes.func,
266
264
 
265
+ /**
266
+ * Cb for when there's some error from any of the promises.
267
+ *
268
+ * @param {Error} error
269
+ */
270
+ onError: PropTypes.func,
271
+
267
272
  /**
268
273
  * Custom validation function
269
274
  * @param {File} file
@@ -320,29 +325,33 @@ export default Dropzone;
320
325
  */
321
326
 
322
327
  /**
323
- * An object with the current dropzone state and some helper functions.
328
+ * An object with the current dropzone state.
324
329
  *
325
330
  * @typedef {object} DropzoneState
326
- * @property {Function} getRootProps Returns the props you should apply to the root drop container you render
327
- * @property {Function} getInputProps Returns the props you should apply to hidden file input you render
328
- * @property {Function} open Open the native file selection dialog
329
331
  * @property {boolean} isFocused Dropzone area is in focus
330
332
  * @property {boolean} isFileDialogActive File dialog is opened
331
333
  * @property {boolean} isDragActive Active drag is in progress
332
334
  * @property {boolean} isDragAccept Dragged files are accepted
333
335
  * @property {boolean} isDragReject Some dragged files are rejected
334
- * @property {File[]} draggedFiles Files in active drag
335
336
  * @property {File[]} acceptedFiles Accepted files
336
337
  * @property {FileRejection[]} fileRejections Rejected files and why they were rejected
337
338
  */
338
339
 
340
+ /**
341
+ * An object with the dropzone methods.
342
+ *
343
+ * @typedef {object} DropzoneMethods
344
+ * @property {Function} getRootProps Returns the props you should apply to the root drop container you render
345
+ * @property {Function} getInputProps Returns the props you should apply to hidden file input you render
346
+ * @property {Function} open Open the native file selection dialog
347
+ */
348
+
339
349
  var initialState = {
340
350
  isFocused: false,
341
351
  isFileDialogActive: false,
342
352
  isDragActive: false,
343
353
  isDragAccept: false,
344
354
  isDragReject: false,
345
- draggedFiles: [],
346
355
  acceptedFiles: [],
347
356
  fileRejections: []
348
357
  };
@@ -368,12 +377,11 @@ var initialState = {
368
377
  * @function useDropzone
369
378
  *
370
379
  * @param {object} props
371
- * @param {string|string[]} [props.accept] Set accepted file types.
372
- * See https://github.com/okonet/attr-accept for more information.
380
+ * @param {import("./utils").AcceptProp} [props.accept] Set accepted file types.
381
+ * Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
373
382
  * Keep in mind that mime type determination is not reliable across platforms. CSV files,
374
383
  * for example, are reported as text/plain under macOS but as application/vnd.ms-excel under
375
- * Windows. In some cases there might not be a mime type set at all.
376
- * See: https://github.com/react-dropzone/react-dropzone/issues/276
384
+ * Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
377
385
  * @param {boolean} [props.multiple=true] Allow drag 'n' drop (or selection from the file dialog) of multiple files
378
386
  * @param {boolean} [props.preventDropOnDocument=true] If false, allow dropped items to take over the current browser window
379
387
  * @param {boolean} [props.noClick=false] If true, disables click to open the native file selection dialog
@@ -396,7 +404,7 @@ var initialState = {
396
404
  * Note that this callback is invoked after the `getFilesFromEvent` callback is done.
397
405
  *
398
406
  * Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and `maxSize` props.
399
- * `accept` must be a valid [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml) according to [input element specification](https://www.w3.org/wiki/HTML/Elements/input/file) or a valid file extension.
407
+ * `accept` must be an object with keys as a valid [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml) according to [input element specification](https://www.w3.org/wiki/HTML/Elements/input/file) and the value an array of file extensions (optional).
400
408
  * If `multiple` is set to false and additional files are dropped,
401
409
  * all files besides the first will be rejected.
402
410
  * Any file which does not have a size in the [`minSize`, `maxSize`] range, will be rejected as well.
@@ -418,37 +426,45 @@ var initialState = {
418
426
  * ```
419
427
  * @param {dropAcceptedCb} [props.onDropAccepted]
420
428
  * @param {dropRejectedCb} [props.onDropRejected]
429
+ * @param {(error: Error) => void} [props.onError]
421
430
  *
422
- * @returns {DropzoneState}
431
+ * @returns {DropzoneState & DropzoneMethods}
423
432
  */
424
433
 
425
434
  export function useDropzone() {
426
- var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
427
-
428
- var _defaultProps$options = _objectSpread(_objectSpread({}, defaultProps), options),
429
- accept = _defaultProps$options.accept,
430
- disabled = _defaultProps$options.disabled,
431
- getFilesFromEvent = _defaultProps$options.getFilesFromEvent,
432
- maxSize = _defaultProps$options.maxSize,
433
- minSize = _defaultProps$options.minSize,
434
- multiple = _defaultProps$options.multiple,
435
- maxFiles = _defaultProps$options.maxFiles,
436
- onDragEnter = _defaultProps$options.onDragEnter,
437
- onDragLeave = _defaultProps$options.onDragLeave,
438
- onDragOver = _defaultProps$options.onDragOver,
439
- onDrop = _defaultProps$options.onDrop,
440
- onDropAccepted = _defaultProps$options.onDropAccepted,
441
- onDropRejected = _defaultProps$options.onDropRejected,
442
- onFileDialogCancel = _defaultProps$options.onFileDialogCancel,
443
- onFileDialogOpen = _defaultProps$options.onFileDialogOpen,
444
- useFsAccessApi = _defaultProps$options.useFsAccessApi,
445
- preventDropOnDocument = _defaultProps$options.preventDropOnDocument,
446
- noClick = _defaultProps$options.noClick,
447
- noKeyboard = _defaultProps$options.noKeyboard,
448
- noDrag = _defaultProps$options.noDrag,
449
- noDragEventsBubbling = _defaultProps$options.noDragEventsBubbling,
450
- validator = _defaultProps$options.validator;
451
-
435
+ var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
436
+
437
+ var _defaultProps$props = _objectSpread(_objectSpread({}, defaultProps), props),
438
+ accept = _defaultProps$props.accept,
439
+ disabled = _defaultProps$props.disabled,
440
+ getFilesFromEvent = _defaultProps$props.getFilesFromEvent,
441
+ maxSize = _defaultProps$props.maxSize,
442
+ minSize = _defaultProps$props.minSize,
443
+ multiple = _defaultProps$props.multiple,
444
+ maxFiles = _defaultProps$props.maxFiles,
445
+ onDragEnter = _defaultProps$props.onDragEnter,
446
+ onDragLeave = _defaultProps$props.onDragLeave,
447
+ onDragOver = _defaultProps$props.onDragOver,
448
+ onDrop = _defaultProps$props.onDrop,
449
+ onDropAccepted = _defaultProps$props.onDropAccepted,
450
+ onDropRejected = _defaultProps$props.onDropRejected,
451
+ onFileDialogCancel = _defaultProps$props.onFileDialogCancel,
452
+ onFileDialogOpen = _defaultProps$props.onFileDialogOpen,
453
+ useFsAccessApi = _defaultProps$props.useFsAccessApi,
454
+ preventDropOnDocument = _defaultProps$props.preventDropOnDocument,
455
+ noClick = _defaultProps$props.noClick,
456
+ noKeyboard = _defaultProps$props.noKeyboard,
457
+ noDrag = _defaultProps$props.noDrag,
458
+ noDragEventsBubbling = _defaultProps$props.noDragEventsBubbling,
459
+ onError = _defaultProps$props.onError,
460
+ validator = _defaultProps$props.validator;
461
+
462
+ var acceptAttr = useMemo(function () {
463
+ return acceptPropAsAcceptAttr(accept);
464
+ }, [accept]);
465
+ var pickerTypes = useMemo(function () {
466
+ return pickerOptionsFromAccept(accept);
467
+ }, [accept]);
452
468
  var onFileDialogOpenCb = useMemo(function () {
453
469
  return typeof onFileDialogOpen === "function" ? onFileDialogOpen : noop;
454
470
  }, [onFileDialogOpen]);
@@ -464,8 +480,7 @@ export function useDropzone() {
464
480
  dispatch = _useReducer2[1];
465
481
 
466
482
  var isFocused = state.isFocused,
467
- isFileDialogActive = state.isFileDialogActive,
468
- draggedFiles = state.draggedFiles;
483
+ isFileDialogActive = state.isFileDialogActive;
469
484
  var fsAccessApiWorksRef = useRef(typeof window !== "undefined" && window.isSecureContext && useFsAccessApi && canUseFileSystemAccessAPI()); // Update file dialog active state when the window is focused on
470
485
 
471
486
  var onWindowFocus = function onWindowFocus() {
@@ -517,6 +532,14 @@ export function useDropzone() {
517
532
  }
518
533
  };
519
534
  }, [rootRef, preventDropOnDocument]);
535
+ var onErrCb = useCallback(function (e) {
536
+ if (onError) {
537
+ onError(e);
538
+ } else {
539
+ // Let the user know something's gone wrong if they haven't provided the onError cb.
540
+ console.error(e);
541
+ }
542
+ }, [onError]);
520
543
  var onDragEnterCb = useCallback(function (event) {
521
544
  event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
522
545
 
@@ -525,13 +548,24 @@ export function useDropzone() {
525
548
  dragTargetsRef.current = [].concat(_toConsumableArray(dragTargetsRef.current), [event.target]);
526
549
 
527
550
  if (isEvtWithFiles(event)) {
528
- Promise.resolve(getFilesFromEvent(event)).then(function (draggedFiles) {
551
+ Promise.resolve(getFilesFromEvent(event)).then(function (files) {
529
552
  if (isPropagationStopped(event) && !noDragEventsBubbling) {
530
553
  return;
531
554
  }
532
555
 
556
+ var fileCount = files.length;
557
+ var isDragAccept = fileCount > 0 && allFilesAccepted({
558
+ files: files,
559
+ accept: acceptAttr,
560
+ minSize: minSize,
561
+ maxSize: maxSize,
562
+ multiple: multiple,
563
+ maxFiles: maxFiles
564
+ });
565
+ var isDragReject = fileCount > 0 && !isDragAccept;
533
566
  dispatch({
534
- draggedFiles: draggedFiles,
567
+ isDragAccept: isDragAccept,
568
+ isDragReject: isDragReject,
535
569
  isDragActive: true,
536
570
  type: "setDraggedFiles"
537
571
  });
@@ -539,9 +573,11 @@ export function useDropzone() {
539
573
  if (onDragEnter) {
540
574
  onDragEnter(event);
541
575
  }
576
+ }).catch(function (e) {
577
+ return onErrCb(e);
542
578
  });
543
579
  }
544
- }, [getFilesFromEvent, onDragEnter, noDragEventsBubbling]);
580
+ }, [getFilesFromEvent, onDragEnter, onErrCb, noDragEventsBubbling, acceptAttr, minSize, maxSize, multiple, maxFiles]);
545
581
  var onDragOverCb = useCallback(function (event) {
546
582
  event.preventDefault();
547
583
  event.persist();
@@ -585,9 +621,10 @@ export function useDropzone() {
585
621
  }
586
622
 
587
623
  dispatch({
588
- isDragActive: false,
589
624
  type: "setDraggedFiles",
590
- draggedFiles: []
625
+ isDragActive: false,
626
+ isDragAccept: false,
627
+ isDragReject: false
591
628
  });
592
629
 
593
630
  if (isEvtWithFiles(event) && onDragLeave) {
@@ -598,7 +635,7 @@ export function useDropzone() {
598
635
  var acceptedFiles = [];
599
636
  var fileRejections = [];
600
637
  files.forEach(function (file) {
601
- var _fileAccepted = fileAccepted(file, accept),
638
+ var _fileAccepted = fileAccepted(file, acceptAttr),
602
639
  _fileAccepted2 = _slicedToArray(_fileAccepted, 2),
603
640
  accepted = _fileAccepted2[0],
604
641
  acceptError = _fileAccepted2[1];
@@ -656,7 +693,7 @@ export function useDropzone() {
656
693
  if (acceptedFiles.length > 0 && onDropAccepted) {
657
694
  onDropAccepted(acceptedFiles, event);
658
695
  }
659
- }, [dispatch, multiple, accept, minSize, maxSize, maxFiles, onDrop, onDropAccepted, onDropRejected, validator]);
696
+ }, [dispatch, multiple, acceptAttr, minSize, maxSize, maxFiles, onDrop, onDropAccepted, onDropRejected, validator]);
660
697
  var onDropCb = useCallback(function (event) {
661
698
  event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
662
699
 
@@ -671,13 +708,15 @@ export function useDropzone() {
671
708
  }
672
709
 
673
710
  setFiles(files, event);
711
+ }).catch(function (e) {
712
+ return onErrCb(e);
674
713
  });
675
714
  }
676
715
 
677
716
  dispatch({
678
717
  type: "reset"
679
718
  });
680
- }, [getFilesFromEvent, setFiles, noDragEventsBubbling]); // Fn for opening the file dialog programmatically
719
+ }, [getFilesFromEvent, setFiles, onErrCb, noDragEventsBubbling]); // Fn for opening the file dialog programmatically
681
720
 
682
721
  var openFileDialog = useCallback(function () {
683
722
  // No point to use FS access APIs if context is not secure
@@ -690,7 +729,7 @@ export function useDropzone() {
690
729
 
691
730
  var opts = {
692
731
  multiple: multiple,
693
- types: filePickerOptionsTypes(accept)
732
+ types: pickerTypes
694
733
  };
695
734
  window.showOpenFilePicker(opts).then(function (handles) {
696
735
  return getFilesFromEvent(handles);
@@ -714,6 +753,8 @@ export function useDropzone() {
714
753
  inputRef.current.value = null;
715
754
  inputRef.current.click();
716
755
  }
756
+ } else {
757
+ onErrCb(e);
717
758
  }
718
759
  });
719
760
  return;
@@ -727,7 +768,7 @@ export function useDropzone() {
727
768
  inputRef.current.value = null;
728
769
  inputRef.current.click();
729
770
  }
730
- }, [dispatch, onFileDialogOpenCb, onFileDialogCancelCb, useFsAccessApi, setFiles, accept, multiple]); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
771
+ }, [dispatch, onFileDialogOpenCb, onFileDialogCancelCb, useFsAccessApi, setFiles, onErrCb, pickerTypes, multiple]); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
731
772
 
732
773
  var onKeyDownCb = useCallback(function (event) {
733
774
  // Ignore keyboard events bubbling up the DOM tree
@@ -829,7 +870,7 @@ export function useDropzone() {
829
870
  rest = _objectWithoutProperties(_ref3, _excluded4);
830
871
 
831
872
  var inputProps = _defineProperty({
832
- accept: accept,
873
+ accept: acceptAttr,
833
874
  multiple: multiple,
834
875
  type: "file",
835
876
  style: {
@@ -843,19 +884,7 @@ export function useDropzone() {
843
884
  return _objectSpread(_objectSpread({}, inputProps), rest);
844
885
  };
845
886
  }, [inputRef, accept, multiple, onDropCb, disabled]);
846
- var fileCount = draggedFiles.length;
847
- var isDragAccept = fileCount > 0 && allFilesAccepted({
848
- files: draggedFiles,
849
- accept: accept,
850
- minSize: minSize,
851
- maxSize: maxSize,
852
- multiple: multiple,
853
- maxFiles: maxFiles
854
- });
855
- var isDragReject = fileCount > 0 && !isDragAccept;
856
887
  return _objectSpread(_objectSpread({}, state), {}, {
857
- isDragAccept: isDragAccept,
858
- isDragReject: isDragReject,
859
888
  isFocused: isFocused && !disabled,
860
889
  getRootProps: getRootProps,
861
890
  getInputProps: getInputProps,
@@ -864,6 +893,11 @@ export function useDropzone() {
864
893
  open: composeHandler(openFileDialog)
865
894
  });
866
895
  }
896
+ /**
897
+ * @param {DropzoneState} state
898
+ * @param {{type: string} & DropzoneState} action
899
+ * @returns {DropzoneState}
900
+ */
867
901
 
868
902
  function reducer(state, action) {
869
903
  /* istanbul ignore next */
@@ -889,12 +923,10 @@ function reducer(state, action) {
889
923
  });
890
924
 
891
925
  case "setDraggedFiles":
892
- /* eslint no-case-declarations: 0 */
893
- var isDragActive = action.isDragActive,
894
- draggedFiles = action.draggedFiles;
895
926
  return _objectSpread(_objectSpread({}, state), {}, {
896
- draggedFiles: draggedFiles,
897
- isDragActive: isDragActive
927
+ isDragActive: action.isDragActive,
928
+ isDragAccept: action.isDragAccept,
929
+ isDragReject: action.isDragReject
898
930
  });
899
931
 
900
932
  case "setFiles":
@@ -1,6 +1,10 @@
1
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
1
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
2
2
 
3
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
3
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
4
+
5
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
6
+
7
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
4
8
 
5
9
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
10
 
@@ -183,25 +187,67 @@ export function canUseFileSystemAccessAPI() {
183
187
  return "showOpenFilePicker" in window;
184
188
  }
185
189
  /**
186
- * filePickerOptionsTypes returns the {types} option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
187
- * based on the accept attr (see https://github.com/react-dropzone/attr-accept)
188
- * E.g: converts ['image/*', 'text/*'] to {'image/*': [], 'text/*': []}
189
- * @param {string|string[]} accept
190
+ * Convert the `{accept}` dropzone prop to the
191
+ * `{types}` option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
192
+ *
193
+ * @param {AcceptProp} accept
194
+ * @returns {{accept: string[]}[]}
190
195
  */
191
196
 
192
- export function filePickerOptionsTypes(accept) {
193
- accept = typeof accept === "string" ? accept.split(",") : accept;
194
- return [{
195
- description: "everything",
196
- // TODO: Need to handle filtering more elegantly than this!
197
- accept: Array.isArray(accept) ? // Accept just MIME types as per spec
198
- // NOTE: accept can be https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers
199
- accept.filter(function (item) {
200
- return item === "audio/*" || item === "video/*" || item === "image/*" || item === "text/*" || /\w+\/[-+.\w]+/g.test(item);
201
- }).reduce(function (a, b) {
202
- return _objectSpread(_objectSpread({}, a), {}, _defineProperty({}, b, []));
203
- }, {}) : {}
204
- }];
197
+ export function pickerOptionsFromAccept(accept) {
198
+ if (isDefined(accept)) {
199
+ return Object.entries(accept).filter(function (_ref2) {
200
+ var _ref3 = _slicedToArray(_ref2, 2),
201
+ mimeType = _ref3[0],
202
+ ext = _ref3[1];
203
+
204
+ var ok = true;
205
+
206
+ if (!isMIMEType(mimeType)) {
207
+ console.warn("Skipped \"".concat(mimeType, "\" because it is not a valid MIME type. Check https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types for a list of valid MIME types."));
208
+ ok = false;
209
+ }
210
+
211
+ if (!Array.isArray(ext) || !ext.every(isExt)) {
212
+ console.warn("Skipped \"".concat(mimeType, "\" because an invalid file extension was provided."));
213
+ ok = false;
214
+ }
215
+
216
+ return ok;
217
+ }).map(function (_ref4) {
218
+ var _ref5 = _slicedToArray(_ref4, 2),
219
+ mimeType = _ref5[0],
220
+ ext = _ref5[1];
221
+
222
+ return {
223
+ accept: _defineProperty({}, mimeType, ext)
224
+ };
225
+ });
226
+ }
227
+
228
+ return accept;
229
+ }
230
+ /**
231
+ * Convert the `{accept}` dropzone prop to an array of MIME types/extensions.
232
+ * @param {AcceptProp} accept
233
+ * @returns {string}
234
+ */
235
+
236
+ export function acceptPropAsAcceptAttr(accept) {
237
+ if (isDefined(accept)) {
238
+ return Object.entries(accept).reduce(function (a, _ref6) {
239
+ var _ref7 = _slicedToArray(_ref6, 2),
240
+ mimeType = _ref7[0],
241
+ ext = _ref7[1];
242
+
243
+ return [].concat(_toConsumableArray(a), [mimeType], _toConsumableArray(ext));
244
+ }, []) // Silently discard invalid entries as pickerOptionsFromAccept warns about these
245
+ .filter(function (v) {
246
+ return isMIMEType(v) || isExt(v);
247
+ }).join(",");
248
+ }
249
+
250
+ return undefined;
205
251
  }
206
252
  /**
207
253
  * Check if v is an exception caused by aborting a request (e.g window.showOpenFilePicker()).
@@ -224,4 +270,26 @@ export function isAbort(v) {
224
270
 
225
271
  export function isSecurityError(v) {
226
272
  return v instanceof DOMException && (v.name === "SecurityError" || v.code === v.SECURITY_ERR);
227
- }
273
+ }
274
+ /**
275
+ * Check if v is a MIME type string.
276
+ *
277
+ * See accepted format: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers.
278
+ *
279
+ * @param {string} v
280
+ */
281
+
282
+ export function isMIMEType(v) {
283
+ return v === "audio/*" || v === "video/*" || v === "image/*" || v === "text/*" || /\w+\/[-+.\w]+/g.test(v);
284
+ }
285
+ /**
286
+ * Check if v is a file extension.
287
+ * @param {string} v
288
+ */
289
+
290
+ export function isExt(v) {
291
+ return /^.*\.[\w]+$/.test(v);
292
+ }
293
+ /**
294
+ * @typedef {Object.<string, string[]>} AcceptProp
295
+ */