react-dropzone 12.1.0 → 13.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 +0 -2
- package/dist/es/index.js +71 -44
- package/dist/es/utils/index.js +88 -20
- package/dist/index.js +3 -3
- package/examples/accept/README.md +19 -16
- package/examples/pintura/README.md +3 -1
- package/examples/previews/README.md +3 -1
- package/examples/styling/README.md +2 -2
- package/package.json +1 -1
- package/src/index.js +69 -42
- package/src/index.spec.js +203 -25
- package/src/utils/index.js +82 -26
- package/src/utils/index.spec.js +124 -58
- package/typings/react-dropzone.d.ts +5 -1
- package/typings/tests/accept.tsx +3 -37
- package/typings/tests/all.tsx +3 -1
package/README.md
CHANGED
|
@@ -343,8 +343,6 @@ As one can imagine, this doesn't really work if there's a lot of files or large
|
|
|
343
343
|
|
|
344
344
|
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
345
|
|
|
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
346
|
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
347
|
|
|
350
348
|
**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,
|
|
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
|
*
|
|
@@ -110,13 +110,12 @@ Dropzone.propTypes = {
|
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Set accepted file types.
|
|
113
|
-
*
|
|
113
|
+
* Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
|
|
114
114
|
* Keep in mind that mime type determination is not reliable across platforms. CSV files,
|
|
115
115
|
* 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
|
|
116
|
+
* Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
|
|
118
117
|
*/
|
|
119
|
-
accept: PropTypes.
|
|
118
|
+
accept: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.string)),
|
|
120
119
|
|
|
121
120
|
/**
|
|
122
121
|
* Allow drag 'n' drop (or selection from the file dialog) of multiple files
|
|
@@ -264,6 +263,13 @@ Dropzone.propTypes = {
|
|
|
264
263
|
*/
|
|
265
264
|
onDropRejected: PropTypes.func,
|
|
266
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Cb for when there's some error from any of the promises.
|
|
268
|
+
*
|
|
269
|
+
* @param {Error} error
|
|
270
|
+
*/
|
|
271
|
+
onError: PropTypes.func,
|
|
272
|
+
|
|
267
273
|
/**
|
|
268
274
|
* Custom validation function
|
|
269
275
|
* @param {File} file
|
|
@@ -368,12 +374,11 @@ var initialState = {
|
|
|
368
374
|
* @function useDropzone
|
|
369
375
|
*
|
|
370
376
|
* @param {object} props
|
|
371
|
-
* @param {
|
|
372
|
-
*
|
|
377
|
+
* @param {import("./utils").AcceptProp} [props.accept] Set accepted file types.
|
|
378
|
+
* Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
|
|
373
379
|
* Keep in mind that mime type determination is not reliable across platforms. CSV files,
|
|
374
380
|
* 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
|
|
381
|
+
* Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
|
|
377
382
|
* @param {boolean} [props.multiple=true] Allow drag 'n' drop (or selection from the file dialog) of multiple files
|
|
378
383
|
* @param {boolean} [props.preventDropOnDocument=true] If false, allow dropped items to take over the current browser window
|
|
379
384
|
* @param {boolean} [props.noClick=false] If true, disables click to open the native file selection dialog
|
|
@@ -396,7 +401,7 @@ var initialState = {
|
|
|
396
401
|
* Note that this callback is invoked after the `getFilesFromEvent` callback is done.
|
|
397
402
|
*
|
|
398
403
|
* 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)
|
|
404
|
+
* `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
405
|
* If `multiple` is set to false and additional files are dropped,
|
|
401
406
|
* all files besides the first will be rejected.
|
|
402
407
|
* Any file which does not have a size in the [`minSize`, `maxSize`] range, will be rejected as well.
|
|
@@ -418,37 +423,45 @@ var initialState = {
|
|
|
418
423
|
* ```
|
|
419
424
|
* @param {dropAcceptedCb} [props.onDropAccepted]
|
|
420
425
|
* @param {dropRejectedCb} [props.onDropRejected]
|
|
426
|
+
* @param {(error: Error) => void} [props.onError]
|
|
421
427
|
*
|
|
422
428
|
* @returns {DropzoneState}
|
|
423
429
|
*/
|
|
424
430
|
|
|
425
431
|
export function useDropzone() {
|
|
426
|
-
var
|
|
427
|
-
|
|
428
|
-
var _defaultProps$
|
|
429
|
-
accept = _defaultProps$
|
|
430
|
-
disabled = _defaultProps$
|
|
431
|
-
getFilesFromEvent = _defaultProps$
|
|
432
|
-
maxSize = _defaultProps$
|
|
433
|
-
minSize = _defaultProps$
|
|
434
|
-
multiple = _defaultProps$
|
|
435
|
-
maxFiles = _defaultProps$
|
|
436
|
-
onDragEnter = _defaultProps$
|
|
437
|
-
onDragLeave = _defaultProps$
|
|
438
|
-
onDragOver = _defaultProps$
|
|
439
|
-
onDrop = _defaultProps$
|
|
440
|
-
onDropAccepted = _defaultProps$
|
|
441
|
-
onDropRejected = _defaultProps$
|
|
442
|
-
onFileDialogCancel = _defaultProps$
|
|
443
|
-
onFileDialogOpen = _defaultProps$
|
|
444
|
-
useFsAccessApi = _defaultProps$
|
|
445
|
-
preventDropOnDocument = _defaultProps$
|
|
446
|
-
noClick = _defaultProps$
|
|
447
|
-
noKeyboard = _defaultProps$
|
|
448
|
-
noDrag = _defaultProps$
|
|
449
|
-
noDragEventsBubbling = _defaultProps$
|
|
450
|
-
|
|
451
|
-
|
|
432
|
+
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
433
|
+
|
|
434
|
+
var _defaultProps$props = _objectSpread(_objectSpread({}, defaultProps), props),
|
|
435
|
+
accept = _defaultProps$props.accept,
|
|
436
|
+
disabled = _defaultProps$props.disabled,
|
|
437
|
+
getFilesFromEvent = _defaultProps$props.getFilesFromEvent,
|
|
438
|
+
maxSize = _defaultProps$props.maxSize,
|
|
439
|
+
minSize = _defaultProps$props.minSize,
|
|
440
|
+
multiple = _defaultProps$props.multiple,
|
|
441
|
+
maxFiles = _defaultProps$props.maxFiles,
|
|
442
|
+
onDragEnter = _defaultProps$props.onDragEnter,
|
|
443
|
+
onDragLeave = _defaultProps$props.onDragLeave,
|
|
444
|
+
onDragOver = _defaultProps$props.onDragOver,
|
|
445
|
+
onDrop = _defaultProps$props.onDrop,
|
|
446
|
+
onDropAccepted = _defaultProps$props.onDropAccepted,
|
|
447
|
+
onDropRejected = _defaultProps$props.onDropRejected,
|
|
448
|
+
onFileDialogCancel = _defaultProps$props.onFileDialogCancel,
|
|
449
|
+
onFileDialogOpen = _defaultProps$props.onFileDialogOpen,
|
|
450
|
+
useFsAccessApi = _defaultProps$props.useFsAccessApi,
|
|
451
|
+
preventDropOnDocument = _defaultProps$props.preventDropOnDocument,
|
|
452
|
+
noClick = _defaultProps$props.noClick,
|
|
453
|
+
noKeyboard = _defaultProps$props.noKeyboard,
|
|
454
|
+
noDrag = _defaultProps$props.noDrag,
|
|
455
|
+
noDragEventsBubbling = _defaultProps$props.noDragEventsBubbling,
|
|
456
|
+
onError = _defaultProps$props.onError,
|
|
457
|
+
validator = _defaultProps$props.validator;
|
|
458
|
+
|
|
459
|
+
var acceptAttr = useMemo(function () {
|
|
460
|
+
return acceptPropAsAcceptAttr(accept);
|
|
461
|
+
}, [accept]);
|
|
462
|
+
var pickerTypes = useMemo(function () {
|
|
463
|
+
return pickerOptionsFromAccept(accept);
|
|
464
|
+
}, [accept]);
|
|
452
465
|
var onFileDialogOpenCb = useMemo(function () {
|
|
453
466
|
return typeof onFileDialogOpen === "function" ? onFileDialogOpen : noop;
|
|
454
467
|
}, [onFileDialogOpen]);
|
|
@@ -517,6 +530,14 @@ export function useDropzone() {
|
|
|
517
530
|
}
|
|
518
531
|
};
|
|
519
532
|
}, [rootRef, preventDropOnDocument]);
|
|
533
|
+
var onErrCb = useCallback(function (e) {
|
|
534
|
+
if (onError) {
|
|
535
|
+
onError(e);
|
|
536
|
+
} else {
|
|
537
|
+
// Let the user know something's gone wrong if they haven't provided the onError cb.
|
|
538
|
+
console.error(e);
|
|
539
|
+
}
|
|
540
|
+
}, [onError]);
|
|
520
541
|
var onDragEnterCb = useCallback(function (event) {
|
|
521
542
|
event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
|
|
522
543
|
|
|
@@ -539,9 +560,11 @@ export function useDropzone() {
|
|
|
539
560
|
if (onDragEnter) {
|
|
540
561
|
onDragEnter(event);
|
|
541
562
|
}
|
|
563
|
+
}).catch(function (e) {
|
|
564
|
+
return onErrCb(e);
|
|
542
565
|
});
|
|
543
566
|
}
|
|
544
|
-
}, [getFilesFromEvent, onDragEnter, noDragEventsBubbling]);
|
|
567
|
+
}, [getFilesFromEvent, onDragEnter, onErrCb, noDragEventsBubbling]);
|
|
545
568
|
var onDragOverCb = useCallback(function (event) {
|
|
546
569
|
event.preventDefault();
|
|
547
570
|
event.persist();
|
|
@@ -598,7 +621,7 @@ export function useDropzone() {
|
|
|
598
621
|
var acceptedFiles = [];
|
|
599
622
|
var fileRejections = [];
|
|
600
623
|
files.forEach(function (file) {
|
|
601
|
-
var _fileAccepted = fileAccepted(file,
|
|
624
|
+
var _fileAccepted = fileAccepted(file, acceptAttr),
|
|
602
625
|
_fileAccepted2 = _slicedToArray(_fileAccepted, 2),
|
|
603
626
|
accepted = _fileAccepted2[0],
|
|
604
627
|
acceptError = _fileAccepted2[1];
|
|
@@ -656,7 +679,7 @@ export function useDropzone() {
|
|
|
656
679
|
if (acceptedFiles.length > 0 && onDropAccepted) {
|
|
657
680
|
onDropAccepted(acceptedFiles, event);
|
|
658
681
|
}
|
|
659
|
-
}, [dispatch, multiple,
|
|
682
|
+
}, [dispatch, multiple, acceptAttr, minSize, maxSize, maxFiles, onDrop, onDropAccepted, onDropRejected, validator]);
|
|
660
683
|
var onDropCb = useCallback(function (event) {
|
|
661
684
|
event.preventDefault(); // Persist here because we need the event later after getFilesFromEvent() is done
|
|
662
685
|
|
|
@@ -671,13 +694,15 @@ export function useDropzone() {
|
|
|
671
694
|
}
|
|
672
695
|
|
|
673
696
|
setFiles(files, event);
|
|
697
|
+
}).catch(function (e) {
|
|
698
|
+
return onErrCb(e);
|
|
674
699
|
});
|
|
675
700
|
}
|
|
676
701
|
|
|
677
702
|
dispatch({
|
|
678
703
|
type: "reset"
|
|
679
704
|
});
|
|
680
|
-
}, [getFilesFromEvent, setFiles, noDragEventsBubbling]); // Fn for opening the file dialog programmatically
|
|
705
|
+
}, [getFilesFromEvent, setFiles, onErrCb, noDragEventsBubbling]); // Fn for opening the file dialog programmatically
|
|
681
706
|
|
|
682
707
|
var openFileDialog = useCallback(function () {
|
|
683
708
|
// No point to use FS access APIs if context is not secure
|
|
@@ -690,7 +715,7 @@ export function useDropzone() {
|
|
|
690
715
|
|
|
691
716
|
var opts = {
|
|
692
717
|
multiple: multiple,
|
|
693
|
-
types:
|
|
718
|
+
types: pickerTypes
|
|
694
719
|
};
|
|
695
720
|
window.showOpenFilePicker(opts).then(function (handles) {
|
|
696
721
|
return getFilesFromEvent(handles);
|
|
@@ -714,6 +739,8 @@ export function useDropzone() {
|
|
|
714
739
|
inputRef.current.value = null;
|
|
715
740
|
inputRef.current.click();
|
|
716
741
|
}
|
|
742
|
+
} else {
|
|
743
|
+
onErrCb(e);
|
|
717
744
|
}
|
|
718
745
|
});
|
|
719
746
|
return;
|
|
@@ -727,7 +754,7 @@ export function useDropzone() {
|
|
|
727
754
|
inputRef.current.value = null;
|
|
728
755
|
inputRef.current.click();
|
|
729
756
|
}
|
|
730
|
-
}, [dispatch, onFileDialogOpenCb, onFileDialogCancelCb, useFsAccessApi, setFiles,
|
|
757
|
+
}, [dispatch, onFileDialogOpenCb, onFileDialogCancelCb, useFsAccessApi, setFiles, onErrCb, pickerTypes, multiple]); // Cb to open the file dialog when SPACE/ENTER occurs on the dropzone
|
|
731
758
|
|
|
732
759
|
var onKeyDownCb = useCallback(function (event) {
|
|
733
760
|
// Ignore keyboard events bubbling up the DOM tree
|
|
@@ -829,7 +856,7 @@ export function useDropzone() {
|
|
|
829
856
|
rest = _objectWithoutProperties(_ref3, _excluded4);
|
|
830
857
|
|
|
831
858
|
var inputProps = _defineProperty({
|
|
832
|
-
accept:
|
|
859
|
+
accept: acceptAttr,
|
|
833
860
|
multiple: multiple,
|
|
834
861
|
type: "file",
|
|
835
862
|
style: {
|
|
@@ -846,7 +873,7 @@ export function useDropzone() {
|
|
|
846
873
|
var fileCount = draggedFiles.length;
|
|
847
874
|
var isDragAccept = fileCount > 0 && allFilesAccepted({
|
|
848
875
|
files: draggedFiles,
|
|
849
|
-
accept:
|
|
876
|
+
accept: acceptAttr,
|
|
850
877
|
minSize: minSize,
|
|
851
878
|
maxSize: maxSize,
|
|
852
879
|
multiple: multiple,
|
package/dist/es/utils/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
function
|
|
1
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
2
2
|
|
|
3
|
-
function
|
|
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
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @param {
|
|
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
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
+
*/
|