react-dropzone 19.3.0 → 20.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/README.md +1 -1
- package/dist/index.cjs +47 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +47 -17
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/index.tsx +39 -16
- package/src/utils/index.ts +57 -15
package/src/index.tsx
CHANGED
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
canUseFileSystemAccessAPI,
|
|
8
8
|
composeEventHandlers,
|
|
9
9
|
ErrorCode,
|
|
10
|
+
evaluateDragFiles,
|
|
10
11
|
fileAccepted,
|
|
11
12
|
fileMatchSize,
|
|
12
13
|
flattenAccept,
|
|
13
|
-
getDragVerdict,
|
|
14
14
|
isAbort,
|
|
15
15
|
isEvtWithFiles,
|
|
16
16
|
isIeOrEdge,
|
|
@@ -22,7 +22,13 @@ import {
|
|
|
22
22
|
pickerOptionsFromAccept,
|
|
23
23
|
TOO_MANY_FILES_REJECTION
|
|
24
24
|
} from "./utils";
|
|
25
|
-
import type {
|
|
25
|
+
import type {
|
|
26
|
+
Accept,
|
|
27
|
+
AcceptGroup,
|
|
28
|
+
DragFileRejection as UtilsDragFileRejection,
|
|
29
|
+
FileError,
|
|
30
|
+
ValidatorResult
|
|
31
|
+
} from "./utils";
|
|
26
32
|
|
|
27
33
|
export type {Accept, AcceptGroup, FileError, FileWithPath, ValidatorResult};
|
|
28
34
|
export {ErrorCode};
|
|
@@ -36,6 +42,8 @@ export interface FileRejection {
|
|
|
36
42
|
errors: readonly FileError[];
|
|
37
43
|
}
|
|
38
44
|
|
|
45
|
+
export type DragFileRejection = UtilsDragFileRejection<FileWithPath | DataTransferItem>;
|
|
46
|
+
|
|
39
47
|
type SharedProps = "multiple" | "onDragEnter" | "onDragOver" | "onDragLeave";
|
|
40
48
|
|
|
41
49
|
export type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, SharedProps> & {
|
|
@@ -116,6 +124,13 @@ export type DropzoneState = DropzoneRef & {
|
|
|
116
124
|
isProcessing: boolean;
|
|
117
125
|
acceptedFiles: readonly FileWithPath[];
|
|
118
126
|
fileRejections: readonly FileRejection[];
|
|
127
|
+
/**
|
|
128
|
+
* Rejections that can be determined before drop. Standard drag events expose
|
|
129
|
+
* `DataTransferItem`s with a MIME type but no name or size, so extension, size, and custom
|
|
130
|
+
* validator failures are only available in {@link fileRejections} after drop. In practice this
|
|
131
|
+
* state can report MIME-type and surplus-file errors during a standard browser drag.
|
|
132
|
+
*/
|
|
133
|
+
dragFileRejections: readonly DragFileRejection[];
|
|
119
134
|
rootRef: React.RefObject<HTMLElement>;
|
|
120
135
|
inputRef: React.RefObject<HTMLInputElement>;
|
|
121
136
|
getRootProps: <T extends DropzoneRootProps>(props?: T) => T;
|
|
@@ -171,6 +186,7 @@ interface DropzoneInternalState {
|
|
|
171
186
|
isProcessing: boolean;
|
|
172
187
|
acceptedFiles: FileWithPath[];
|
|
173
188
|
fileRejections: FileRejection[];
|
|
189
|
+
dragFileRejections: DragFileRejection[];
|
|
174
190
|
}
|
|
175
191
|
|
|
176
192
|
/**
|
|
@@ -196,7 +212,8 @@ const initialState: DropzoneInternalState = {
|
|
|
196
212
|
isDragGlobal: false,
|
|
197
213
|
isProcessing: false,
|
|
198
214
|
acceptedFiles: [],
|
|
199
|
-
fileRejections: []
|
|
215
|
+
fileRejections: [],
|
|
216
|
+
dragFileRejections: []
|
|
200
217
|
};
|
|
201
218
|
|
|
202
219
|
/**
|
|
@@ -459,28 +476,30 @@ export function useDropzone(props: DropzoneOptions = {}): DropzoneState {
|
|
|
459
476
|
return;
|
|
460
477
|
}
|
|
461
478
|
|
|
462
|
-
const fileCount = files.length;
|
|
463
479
|
// During a drag we only have DataTransferItems (MIME type, no name/size), so the
|
|
464
480
|
// custom validator can't run yet - a validator-configured dropzone is "unknown" until
|
|
465
|
-
// drop rather than a misleading accept/reject.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
481
|
+
// drop rather than a misleading accept/reject. The verdict and rejection details come
|
|
482
|
+
// from one evaluation so the two states cannot disagree.
|
|
483
|
+
const evaluation =
|
|
484
|
+
files.length > 0
|
|
485
|
+
? evaluateDragFiles({
|
|
486
|
+
files: files as Array<FileWithPath | DataTransferItem>,
|
|
470
487
|
accept: acceptAttr,
|
|
471
488
|
minSize,
|
|
472
489
|
maxSize,
|
|
473
490
|
multiple,
|
|
474
491
|
maxFiles,
|
|
475
|
-
validator
|
|
492
|
+
validator,
|
|
493
|
+
getErrorMessage
|
|
476
494
|
})
|
|
477
495
|
: null;
|
|
478
496
|
|
|
479
497
|
dispatch({
|
|
480
|
-
isDragAccept: verdict === "accept",
|
|
481
|
-
isDragReject: verdict === "reject",
|
|
482
|
-
isDragUnknown: verdict === "unknown",
|
|
498
|
+
isDragAccept: evaluation?.verdict === "accept",
|
|
499
|
+
isDragReject: evaluation?.verdict === "reject",
|
|
500
|
+
isDragUnknown: evaluation?.verdict === "unknown",
|
|
483
501
|
isDragActive: true,
|
|
502
|
+
dragFileRejections: evaluation?.rejections ?? [],
|
|
484
503
|
type: "setDraggedFiles"
|
|
485
504
|
});
|
|
486
505
|
|
|
@@ -501,7 +520,8 @@ export function useDropzone(props: DropzoneOptions = {}): DropzoneState {
|
|
|
501
520
|
maxSize,
|
|
502
521
|
multiple,
|
|
503
522
|
maxFiles,
|
|
504
|
-
validator
|
|
523
|
+
validator,
|
|
524
|
+
getErrorMessage
|
|
505
525
|
]
|
|
506
526
|
);
|
|
507
527
|
|
|
@@ -558,7 +578,8 @@ export function useDropzone(props: DropzoneOptions = {}): DropzoneState {
|
|
|
558
578
|
isDragActive: false,
|
|
559
579
|
isDragAccept: false,
|
|
560
580
|
isDragReject: false,
|
|
561
|
-
isDragUnknown: false
|
|
581
|
+
isDragUnknown: false,
|
|
582
|
+
dragFileRejections: []
|
|
562
583
|
});
|
|
563
584
|
|
|
564
585
|
if (isEvtWithFiles(event) && onDragLeave) {
|
|
@@ -1058,7 +1079,8 @@ function reducer(state: DropzoneInternalState, action: any): DropzoneInternalSta
|
|
|
1058
1079
|
isDragActive: action.isDragActive,
|
|
1059
1080
|
isDragAccept: action.isDragAccept,
|
|
1060
1081
|
isDragReject: action.isDragReject,
|
|
1061
|
-
isDragUnknown: action.isDragUnknown
|
|
1082
|
+
isDragUnknown: action.isDragUnknown,
|
|
1083
|
+
dragFileRejections: action.dragFileRejections
|
|
1062
1084
|
};
|
|
1063
1085
|
case "setProcessing":
|
|
1064
1086
|
return {
|
|
@@ -1070,6 +1092,7 @@ function reducer(state: DropzoneInternalState, action: any): DropzoneInternalSta
|
|
|
1070
1092
|
...state,
|
|
1071
1093
|
acceptedFiles: action.acceptedFiles,
|
|
1072
1094
|
fileRejections: action.fileRejections,
|
|
1095
|
+
dragFileRejections: [],
|
|
1073
1096
|
isProcessing: false,
|
|
1074
1097
|
isDragReject: false,
|
|
1075
1098
|
isDragUnknown: false
|
package/src/utils/index.ts
CHANGED
|
@@ -207,6 +207,19 @@ export function allFilesAccepted({
|
|
|
207
207
|
*/
|
|
208
208
|
export type DragVerdict = "accept" | "reject" | "unknown";
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* A file rejected by the checks that can run during a drag.
|
|
212
|
+
*/
|
|
213
|
+
export interface DragFileRejection<T extends File | DataTransferItem = File | DataTransferItem> {
|
|
214
|
+
file: T;
|
|
215
|
+
errors: readonly FileError[];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface DragEvaluation<T extends File | DataTransferItem = File | DataTransferItem> {
|
|
219
|
+
verdict: DragVerdict;
|
|
220
|
+
rejections: DragFileRejection<T>[];
|
|
221
|
+
}
|
|
222
|
+
|
|
210
223
|
/**
|
|
211
224
|
* Classify a set of dragged files for the `isDragAccept`/`isDragReject`/`isDragUnknown` states.
|
|
212
225
|
*
|
|
@@ -224,16 +237,17 @@ export type DragVerdict = "accept" | "reject" | "unknown";
|
|
|
224
237
|
*
|
|
225
238
|
* The full check (including the `validator`) still runs on drop in {@link fileAccepted}/`setFiles`.
|
|
226
239
|
*/
|
|
227
|
-
export function
|
|
240
|
+
export function evaluateDragFiles<T extends File | DataTransferItem>({
|
|
228
241
|
files,
|
|
229
242
|
accept,
|
|
230
243
|
minSize,
|
|
231
244
|
maxSize,
|
|
232
245
|
multiple,
|
|
233
246
|
maxFiles = 0,
|
|
234
|
-
validator
|
|
247
|
+
validator,
|
|
248
|
+
getErrorMessage
|
|
235
249
|
}: {
|
|
236
|
-
files:
|
|
250
|
+
files: T[];
|
|
237
251
|
accept?: string;
|
|
238
252
|
minSize?: number;
|
|
239
253
|
maxSize?: number;
|
|
@@ -242,24 +256,52 @@ export function getDragVerdict({
|
|
|
242
256
|
// The validator is never invoked here (see the note above), so its async variant is accepted
|
|
243
257
|
// purely so the same `validator` prop is assignable during a drag.
|
|
244
258
|
validator?: (file: File) => ValidatorResult | Promise<ValidatorResult>;
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
259
|
+
getErrorMessage?: (error: FileError, file: File) => string;
|
|
260
|
+
}): DragEvaluation<T> {
|
|
261
|
+
const acceptedFiles: T[] = [];
|
|
262
|
+
const rejections: DragFileRejection<T>[] = [];
|
|
263
|
+
|
|
264
|
+
const localizeError = (error: FileError, file: T): FileError =>
|
|
265
|
+
getErrorMessage && typeof File !== "undefined" && file instanceof File
|
|
266
|
+
? {...error, message: getErrorMessage(error, file)}
|
|
267
|
+
: error;
|
|
268
|
+
|
|
269
|
+
files.forEach(file => {
|
|
270
|
+
const [accepted, acceptError] = fileAccepted(file as File, accept);
|
|
271
|
+
const [sizeMatch, sizeError] = fileMatchSize(file as File, minSize, maxSize);
|
|
272
|
+
|
|
273
|
+
if (accepted && sizeMatch) {
|
|
274
|
+
acceptedFiles.push(file);
|
|
275
|
+
} else {
|
|
276
|
+
rejections.push({
|
|
277
|
+
file,
|
|
278
|
+
errors: [acceptError, sizeError]
|
|
279
|
+
.filter((error): error is FileError => error != null)
|
|
280
|
+
.map(error => localizeError(error, file))
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Match the drop path: files that already failed a per-file check do not consume the limit,
|
|
286
|
+
// and only otherwise-valid surplus files receive a too-many-files rejection.
|
|
287
|
+
const acceptedFilesLimit = multiple ? (maxFiles >= 1 ? maxFiles : Number.POSITIVE_INFINITY) : 1;
|
|
288
|
+
if (acceptedFiles.length > acceptedFilesLimit) {
|
|
289
|
+
acceptedFiles.slice(acceptedFilesLimit).forEach(file => {
|
|
290
|
+
rejections.push({file, errors: [localizeError(TOO_MANY_FILES_REJECTION, file)]});
|
|
291
|
+
});
|
|
249
292
|
}
|
|
250
293
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const [sizeMatch] = fileMatchSize(file as File, minSize, maxSize);
|
|
254
|
-
return !accepted || !sizeMatch;
|
|
255
|
-
});
|
|
256
|
-
if (confidentlyRejected) {
|
|
257
|
-
return "reject";
|
|
294
|
+
if (rejections.length > 0) {
|
|
295
|
+
return {verdict: "reject", rejections};
|
|
258
296
|
}
|
|
259
297
|
|
|
260
298
|
// Built-in checks pass. A custom validator can only ever add rejections on drop, never rescue
|
|
261
299
|
// one - so with a validator present the drag outcome is unknown until we have real Files.
|
|
262
|
-
return validator ? "unknown" : "accept";
|
|
300
|
+
return {verdict: validator ? "unknown" : "accept", rejections};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function getDragVerdict(options: Parameters<typeof evaluateDragFiles>[0]): DragVerdict {
|
|
304
|
+
return evaluateDragFiles(options).verdict;
|
|
263
305
|
}
|
|
264
306
|
|
|
265
307
|
// React's synthetic events has event.isPropagationStopped,
|