react-dropzone
Advanced tools
Comparing version
@@ -31,3 +31,3 @@ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } | ||
import { fromEvent } from 'file-selector'; | ||
import { allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, isEvtWithFiles, isIeOrEdge, isPropagationStopped, onDocumentDragOver } from './utils/index'; | ||
import { allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, isEvtWithFiles, isIeOrEdge, isPropagationStopped, onDocumentDragOver, TOO_MANY_FILES_REJECTION } from './utils/index'; | ||
/** | ||
@@ -82,3 +82,3 @@ * Convenience wrapper component for the `useDropzone` hook | ||
* @param {File[]} params.acceptedFiles Accepted files | ||
* @param {File[]} params.rejectedFiles Rejected files | ||
* @param {FileRejection[]} params.fileRejections Rejected files and why they were rejected | ||
*/ | ||
@@ -203,3 +203,3 @@ children: PropTypes.func, | ||
* @param {File[]} acceptedFiles | ||
* @param {File[]} rejectedFiles | ||
* @param {FileRejection[]} fileRejections | ||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog) | ||
@@ -222,3 +222,3 @@ */ | ||
* | ||
* @param {object[]} files | ||
* @param {FileRejection[]} fileRejections | ||
* @param {(DragEvent|Event)} event | ||
@@ -244,3 +244,3 @@ */ | ||
* @param {File[]} acceptedFiles List of accepted files | ||
* @param {File[]} rejectedFiles List of rejected files | ||
* @param {FileRejection[]} fileRejections List of rejected files and why they were rejected | ||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog) | ||
@@ -291,3 +291,3 @@ */ | ||
* @property {File[]} acceptedFiles Accepted files | ||
* @property {File[]} rejectedFiles Rejected files | ||
* @property {FileRejection[]} fileRejections Rejected files and why they were rejected | ||
*/ | ||
@@ -303,3 +303,3 @@ | ||
acceptedFiles: [], | ||
rejectedFiles: [] | ||
fileRejections: [] | ||
}; | ||
@@ -607,8 +607,24 @@ /** | ||
var acceptedFiles = []; | ||
var rejectedFiles = []; | ||
var fileRejections = []; | ||
files.forEach(function (file) { | ||
if (fileAccepted(file, accept) && fileMatchSize(file, minSize, maxSize)) { | ||
var _fileAccepted = fileAccepted(file, accept), | ||
_fileAccepted2 = _slicedToArray(_fileAccepted, 2), | ||
accepted = _fileAccepted2[0], | ||
acceptError = _fileAccepted2[1]; | ||
var _fileMatchSize = fileMatchSize(file, minSize, maxSize), | ||
_fileMatchSize2 = _slicedToArray(_fileMatchSize, 2), | ||
sizeMatch = _fileMatchSize2[0], | ||
sizeError = _fileMatchSize2[1]; | ||
if (accepted && sizeMatch) { | ||
acceptedFiles.push(file); | ||
} else { | ||
rejectedFiles.push(file); | ||
var errors = [acceptError, sizeError].filter(function (e) { | ||
return e; | ||
}); | ||
fileRejections.push({ | ||
file: file, | ||
errors: errors | ||
}); | ||
} | ||
@@ -618,3 +634,10 @@ }); | ||
if (!multiple && acceptedFiles.length > 1) { | ||
rejectedFiles.push.apply(rejectedFiles, _toConsumableArray(acceptedFiles.splice(0))); // Reject everything and empty accepted files | ||
// Reject everything and empty accepted files | ||
acceptedFiles.forEach(function (file) { | ||
fileRejections.push({ | ||
file: file, | ||
errors: [TOO_MANY_FILES_REJECTION] | ||
}); | ||
}); | ||
acceptedFiles.splice(0); | ||
} | ||
@@ -624,3 +647,3 @@ | ||
acceptedFiles: acceptedFiles, | ||
rejectedFiles: rejectedFiles, | ||
fileRejections: fileRejections, | ||
type: 'setFiles' | ||
@@ -630,7 +653,7 @@ }); | ||
if (onDrop) { | ||
onDrop(acceptedFiles, rejectedFiles, event); | ||
onDrop(acceptedFiles, fileRejections, event); | ||
} | ||
if (rejectedFiles.length > 0 && onDropRejected) { | ||
onDropRejected(rejectedFiles, event); | ||
if (fileRejections.length > 0 && onDropRejected) { | ||
onDropRejected(fileRejections, event); | ||
} | ||
@@ -780,3 +803,3 @@ | ||
acceptedFiles: action.acceptedFiles, | ||
rejectedFiles: action.rejectedFiles | ||
fileRejections: action.fileRejections | ||
}); | ||
@@ -790,3 +813,3 @@ | ||
acceptedFiles: [], | ||
rejectedFiles: [] | ||
fileRejections: [] | ||
}); | ||
@@ -793,0 +816,0 @@ |
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
import accepts from 'attr-accept'; // Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } | ||
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
import accepts from 'attr-accept'; // Error codes | ||
export var FILE_INVALID_TYPE = 'file-invalid-type'; | ||
export var FILE_TOO_LARGE = 'file-too-large'; | ||
export var FILE_TOO_SMALL = 'file-too-small'; | ||
export var TOO_MANY_FILES = 'too-many-files'; // File Errors | ||
export var getInvalidTypeRejectionErr = function getInvalidTypeRejectionErr(accept) { | ||
accept = Array.isArray(accept) && accept.length === 1 ? accept[0] : accept; | ||
var messageSuffix = Array.isArray(accept) ? "one of ".concat(accept.join(', ')) : accept; | ||
return { | ||
code: FILE_INVALID_TYPE, | ||
message: "File type must be ".concat(messageSuffix) | ||
}; | ||
}; | ||
export var getTooLargeRejectionErr = function getTooLargeRejectionErr(maxSize) { | ||
return { | ||
code: FILE_TOO_LARGE, | ||
message: "File is larger than ".concat(maxSize, " bytes") | ||
}; | ||
}; | ||
export var getTooSmallRejectionErr = function getTooSmallRejectionErr(minSize) { | ||
return { | ||
code: FILE_TOO_SMALL, | ||
message: "File is smaller than ".concat(minSize, " bytes") | ||
}; | ||
}; | ||
export var TOO_MANY_FILES_REJECTION = { | ||
code: TOO_MANY_FILES, | ||
message: 'Too many files' | ||
}; // Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with | ||
// that MIME type will always be accepted | ||
export function fileAccepted(file, accept) { | ||
return file.type === 'application/x-moz-file' || accepts(file, accept); | ||
var isAcceptable = file.type === 'application/x-moz-file' || accepts(file, accept); | ||
return [isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept)]; | ||
} | ||
export function fileMatchSize(file, minSize, maxSize) { | ||
if (isDefined(file.size)) { | ||
if (isDefined(minSize) && isDefined(maxSize)) return file.size >= minSize && file.size <= maxSize;else if (isDefined(minSize)) return file.size >= minSize;else if (isDefined(maxSize)) return file.size <= maxSize; | ||
if (isDefined(minSize) && isDefined(maxSize)) { | ||
if (file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)]; | ||
if (file.size < minSize) return [false, getTooSmallRejectionErr(minSize)]; | ||
} else if (isDefined(minSize) && file.size < minSize) return [false, getTooSmallRejectionErr(minSize)];else if (isDefined(maxSize) && file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)]; | ||
} | ||
return true; | ||
return [true, null]; | ||
} | ||
@@ -33,3 +75,11 @@ | ||
return files.every(function (file) { | ||
return fileAccepted(file, accept) && fileMatchSize(file, minSize, maxSize); | ||
var _fileAccepted = fileAccepted(file, accept), | ||
_fileAccepted2 = _slicedToArray(_fileAccepted, 1), | ||
accepted = _fileAccepted2[0]; | ||
var _fileMatchSize = fileMatchSize(file, minSize, maxSize), | ||
_fileMatchSize2 = _slicedToArray(_fileMatchSize, 1), | ||
sizeMatch = _fileMatchSize2[0]; | ||
return accepted && sizeMatch; | ||
}); | ||
@@ -36,0 +86,0 @@ } // React's synthetic events has event.isPropagationStopped, |
@@ -168,3 +168,3 @@ { | ||
}, | ||
"version": "10.2.2", | ||
"version": "11.0.0", | ||
"engines": { | ||
@@ -171,0 +171,0 @@ "node": ">= 8" |
@@ -22,3 +22,4 @@ /* eslint prefer-template: 0 */ | ||
isPropagationStopped, | ||
onDocumentDragOver | ||
onDocumentDragOver, | ||
TOO_MANY_FILES_REJECTION | ||
} from './utils/index' | ||
@@ -65,3 +66,3 @@ | ||
* @param {File[]} params.acceptedFiles Accepted files | ||
* @param {File[]} params.rejectedFiles Rejected files | ||
* @param {FileRejection[]} params.fileRejections Rejected files and why they were rejected | ||
*/ | ||
@@ -186,3 +187,3 @@ children: PropTypes.func, | ||
* @param {File[]} acceptedFiles | ||
* @param {File[]} rejectedFiles | ||
* @param {FileRejection[]} fileRejections | ||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog) | ||
@@ -205,3 +206,3 @@ */ | ||
* | ||
* @param {object[]} files | ||
* @param {FileRejection[]} fileRejections | ||
* @param {(DragEvent|Event)} event | ||
@@ -229,3 +230,3 @@ */ | ||
* @param {File[]} acceptedFiles List of accepted files | ||
* @param {File[]} rejectedFiles List of rejected files | ||
* @param {FileRejection[]} fileRejections List of rejected files and why they were rejected | ||
* @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog) | ||
@@ -276,3 +277,3 @@ */ | ||
* @property {File[]} acceptedFiles Accepted files | ||
* @property {File[]} rejectedFiles Rejected files | ||
* @property {FileRejection[]} fileRejections Rejected files and why they were rejected | ||
*/ | ||
@@ -288,3 +289,3 @@ | ||
acceptedFiles: [], | ||
rejectedFiles: [] | ||
fileRejections: [] | ||
} | ||
@@ -587,9 +588,12 @@ | ||
const acceptedFiles = [] | ||
const rejectedFiles = [] | ||
const fileRejections = [] | ||
files.forEach(file => { | ||
if (fileAccepted(file, accept) && fileMatchSize(file, minSize, maxSize)) { | ||
const [accepted, acceptError] = fileAccepted(file, accept) | ||
const [sizeMatch, sizeError] = fileMatchSize(file, minSize, maxSize) | ||
if (accepted && sizeMatch) { | ||
acceptedFiles.push(file) | ||
} else { | ||
rejectedFiles.push(file) | ||
const errors = [acceptError, sizeError].filter(e => e) | ||
fileRejections.push({ file, errors }) | ||
} | ||
@@ -599,3 +603,7 @@ }) | ||
if (!multiple && acceptedFiles.length > 1) { | ||
rejectedFiles.push(...acceptedFiles.splice(0)) // Reject everything and empty accepted files | ||
// Reject everything and empty accepted files | ||
acceptedFiles.forEach(file => { | ||
fileRejections.push({ file, errors: [TOO_MANY_FILES_REJECTION] }) | ||
}) | ||
acceptedFiles.splice(0) | ||
} | ||
@@ -605,3 +613,3 @@ | ||
acceptedFiles, | ||
rejectedFiles, | ||
fileRejections, | ||
type: 'setFiles' | ||
@@ -611,7 +619,7 @@ }) | ||
if (onDrop) { | ||
onDrop(acceptedFiles, rejectedFiles, event) | ||
onDrop(acceptedFiles, fileRejections, event) | ||
} | ||
if (rejectedFiles.length > 0 && onDropRejected) { | ||
onDropRejected(rejectedFiles, event) | ||
if (fileRejections.length > 0 && onDropRejected) { | ||
onDropRejected(fileRejections, event) | ||
} | ||
@@ -776,3 +784,3 @@ | ||
acceptedFiles: action.acceptedFiles, | ||
rejectedFiles: action.rejectedFiles | ||
fileRejections: action.fileRejections | ||
} | ||
@@ -786,3 +794,3 @@ case 'reset': | ||
acceptedFiles: [], | ||
rejectedFiles: [], | ||
fileRejections: [], | ||
} | ||
@@ -789,0 +797,0 @@ default: |
import accepts from 'attr-accept' | ||
// Error codes | ||
export const FILE_INVALID_TYPE = 'file-invalid-type' | ||
export const FILE_TOO_LARGE = 'file-too-large' | ||
export const FILE_TOO_SMALL = 'file-too-small' | ||
export const TOO_MANY_FILES = 'too-many-files' | ||
// File Errors | ||
export const getInvalidTypeRejectionErr = accept => { | ||
accept = Array.isArray(accept) && accept.length === 1 ? accept[0] : accept | ||
const messageSuffix = Array.isArray(accept) ? `one of ${accept.join(', ')}` : accept | ||
return { | ||
code: FILE_INVALID_TYPE, | ||
message: `File type must be ${messageSuffix}` | ||
} | ||
} | ||
export const getTooLargeRejectionErr = maxSize => { | ||
return { | ||
code: FILE_TOO_LARGE, | ||
message: `File is larger than ${maxSize} bytes` | ||
} | ||
} | ||
export const getTooSmallRejectionErr = minSize => { | ||
return { | ||
code: FILE_TOO_SMALL, | ||
message: `File is smaller than ${minSize} bytes` | ||
} | ||
} | ||
export const TOO_MANY_FILES_REJECTION = { | ||
code: TOO_MANY_FILES, | ||
message: 'Too many files' | ||
} | ||
// Firefox versions prior to 53 return a bogus MIME type for every file drag, so dragovers with | ||
// that MIME type will always be accepted | ||
export function fileAccepted(file, accept) { | ||
return file.type === 'application/x-moz-file' || accepts(file, accept) | ||
const isAcceptable = file.type === 'application/x-moz-file' || accepts(file, accept) | ||
return [isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept)] | ||
} | ||
@@ -11,10 +47,11 @@ | ||
if (isDefined(file.size)) { | ||
if (isDefined(minSize) && isDefined(maxSize)) | ||
return file.size >= minSize && file.size <= maxSize | ||
else if (isDefined(minSize)) | ||
return file.size >= minSize | ||
else if (isDefined(maxSize)) | ||
return file.size <= maxSize | ||
if (isDefined(minSize) && isDefined(maxSize)) { | ||
if (file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)] | ||
if (file.size < minSize) return [false, getTooSmallRejectionErr(minSize)] | ||
} else if (isDefined(minSize) && file.size < minSize) | ||
return [false, getTooSmallRejectionErr(minSize)] | ||
else if (isDefined(maxSize) && file.size > maxSize) | ||
return [false, getTooLargeRejectionErr(maxSize)] | ||
} | ||
return true | ||
return [true, null] | ||
} | ||
@@ -32,3 +69,5 @@ | ||
return files.every(file => { | ||
return fileAccepted(file, accept) && fileMatchSize(file, minSize, maxSize) | ||
const [accepted] = fileAccepted(file, accept) | ||
const [sizeMatch] = fileMatchSize(file, minSize, maxSize) | ||
return accepted && sizeMatch | ||
}) | ||
@@ -35,0 +74,0 @@ } |
@@ -14,28 +14,28 @@ beforeEach(() => { | ||
it('should return true if the file object doesn\'t have a {size} property', () => { | ||
expect(utils.fileMatchSize({})).toBe(true) | ||
expect(utils.fileMatchSize({size: null})).toBe(true) | ||
expect(utils.fileMatchSize({})).toEqual([true, null]) | ||
expect(utils.fileMatchSize({size: null})).toEqual([true, null]) | ||
}) | ||
it('should return true if the minSize and maxSize were not provided', () => { | ||
expect(utils.fileMatchSize({size: 100})).toBe(true) | ||
expect(utils.fileMatchSize({size: 100}, undefined, undefined)).toBe(true) | ||
expect(utils.fileMatchSize({size: 100}, null, null)).toBe(true) | ||
expect(utils.fileMatchSize({size: 100})).toEqual([true, null]) | ||
expect(utils.fileMatchSize({size: 100}, undefined, undefined)).toEqual([true, null]) | ||
expect(utils.fileMatchSize({size: 100}, null, null)).toEqual([true, null]) | ||
}) | ||
it('should return true if the file {size} is within the [minSize, maxSize] range', () => { | ||
expect(utils.fileMatchSize({size: 100}, 10, 200)).toBe(true) | ||
expect(utils.fileMatchSize({size: 100}, 10, 99)).toBe(false) | ||
expect(utils.fileMatchSize({size: 100}, 101, 200)).toBe(false) | ||
expect(utils.fileMatchSize({size: 100}, 10, 200)).toEqual([true, null]) | ||
expect(utils.fileMatchSize({size: 100}, 10, 99)).toEqual([false, { code: 'file-too-large', message: 'File is larger than 99 bytes' }]) | ||
expect(utils.fileMatchSize({size: 100}, 101, 200)).toEqual([false, { code: 'file-too-small', message: 'File is smaller than 101 bytes' }]) | ||
}) | ||
it('should return true if the file {size} is more than minSize', () => { | ||
expect(utils.fileMatchSize({size: 100}, 100)).toBe(true) | ||
expect(utils.fileMatchSize({size: 100}, 101)).toBe(false) | ||
expect(utils.fileMatchSize({size: 100}, 100)).toEqual([true, null]) | ||
expect(utils.fileMatchSize({size: 100}, 101)).toEqual([false, { code: 'file-too-small', message: 'File is smaller than 101 bytes' }]) | ||
}) | ||
it('should return true if the file {size} is less than maxSize', () => { | ||
expect(utils.fileMatchSize({size: 100}, undefined, 100)).toBe(true) | ||
expect(utils.fileMatchSize({size: 100}, null, 100)).toBe(true) | ||
expect(utils.fileMatchSize({size: 100}, undefined, 99)).toBe(false) | ||
expect(utils.fileMatchSize({size: 100}, null, 99)).toBe(false) | ||
expect(utils.fileMatchSize({size: 100}, undefined, 100)).toEqual([true, null]) | ||
expect(utils.fileMatchSize({size: 100}, null, 100)).toEqual([true, null]) | ||
expect(utils.fileMatchSize({size: 100}, undefined, 99)).toEqual([false, { code: 'file-too-large', message: 'File is larger than 99 bytes' }]) | ||
expect(utils.fileMatchSize({size: 100}, null, 99)).toEqual([false, { code: 'file-too-large', message: 'File is larger than 99 bytes' }]) | ||
}) | ||
@@ -194,1 +194,51 @@ }) | ||
}) | ||
describe('fileAccepted', () => { | ||
let utils | ||
beforeEach(async done => { | ||
utils = await import('./index') | ||
done() | ||
}) | ||
it('accepts bogus firefox file', () => { | ||
const file = createFile('bogus.png', 100, 'application/x-moz-file'); | ||
expect(utils.fileAccepted(file, '.pdf')).toEqual([true, null]) | ||
}) | ||
it('accepts file when single accept criteria', () => { | ||
const file = createFile('hamster.pdf', 100, 'application/pdf'); | ||
expect(utils.fileAccepted(file, '.pdf')).toEqual([true, null]) | ||
}) | ||
it('accepts file when multiple accept criteria', () => { | ||
const file = createFile('hamster.pdf', 100, 'application/pdf'); | ||
expect(utils.fileAccepted(file, ['.pdf', '.png'])).toEqual([true, null]) | ||
}) | ||
it('rejects file when single accept criteria', () => { | ||
const file = createFile('hamster.pdf', 100, 'application/pdf'); | ||
expect(utils.fileAccepted(file, '.png')).toEqual([false, { code: 'file-invalid-type', message: 'File type must be .png' }]) | ||
}) | ||
it('rejects file when multiple accept criteria', () => { | ||
const file = createFile('hamster.pdf', 100, 'application/pdf'); | ||
expect(utils.fileAccepted(file, ['.gif', '.png'])).toEqual([false, { code: 'file-invalid-type', message: 'File type must be one of .gif, .png' }]) | ||
}) | ||
it('rejects file when single accept criteria as array', () => { | ||
const file = createFile('hamster.pdf', 100, 'application/pdf'); | ||
expect(utils.fileAccepted(file, ['.gif'])).toEqual([false, { code: 'file-invalid-type', message: 'File type must be .gif' }]) | ||
}) | ||
}) | ||
function createFile(name, size, type) { | ||
const file = new File([], name, { type }) | ||
Object.defineProperty(file, 'size', { | ||
get() { | ||
return size | ||
} | ||
}) | ||
return file | ||
} | ||
@@ -11,2 +11,12 @@ import * as React from "react"; | ||
export interface FileError { | ||
message: string; | ||
code: string; | ||
} | ||
export interface FileRejection { | ||
file: File; | ||
errors: FileError[]; | ||
} | ||
export type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, PropTypes> & { | ||
@@ -22,5 +32,5 @@ accept?: string | string[]; | ||
disabled?: boolean; | ||
onDrop?<T extends File>(acceptedFiles: T[], rejectedFiles: T[], event: DropEvent): void; | ||
onDrop?<T extends File>(acceptedFiles: T[], fileRejections: FileRejection[], event: DropEvent): void; | ||
onDropAccepted?<T extends File>(files: T[], event: DropEvent): void; | ||
onDropRejected?<T extends File>(files: T[], event: DropEvent): void; | ||
onDropRejected?(fileRejections: FileRejection[], event: DropEvent): void; | ||
getFilesFromEvent?(event: DropEvent): Promise<Array<File | DataTransferItem>>; | ||
@@ -40,3 +50,3 @@ onFileDialogCancel?(): void; | ||
acceptedFiles: File[]; | ||
rejectedFiles: File[]; | ||
fileRejections: FileRejection[]; | ||
rootRef: React.RefObject<HTMLElement>; | ||
@@ -43,0 +53,0 @@ inputRef: React.RefObject<HTMLInputElement>; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
326532
4.44%5183
5.5%