react-dropzone 19.1.1 → 19.3.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 +26 -0
- package/dist/index.cjs +114 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -4
- package/dist/index.d.ts +30 -4
- package/dist/index.js +114 -14
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/index.tsx +74 -7
- package/src/utils/index.ts +159 -35
package/dist/index.d.cts
CHANGED
|
@@ -3,9 +3,27 @@ import * as React from "react";
|
|
|
3
3
|
//#region src/utils/index.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* A map of accepted MIME types to file extensions, as passed to the `accept` prop.
|
|
6
|
+
*
|
|
7
|
+
* An extension value may be a single extension string or an array of them - both are
|
|
8
|
+
* accepted, mirroring the shape of `window.showOpenFilePicker`'s `accept`.
|
|
6
9
|
*/
|
|
7
10
|
interface Accept {
|
|
8
|
-
[key: string]: readonly string[];
|
|
11
|
+
[key: string]: string | readonly string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A labeled group of accepted types, mirroring one entry of `window.showOpenFilePicker`'s
|
|
15
|
+
* `types` option. Passing the `accept` prop as an array of these lets the File System Access
|
|
16
|
+
* picker present multiple named filter rows instead of a single one (see
|
|
17
|
+
* {@link pickerOptionsFromAccept}). The optional `description` labels the row; when omitted it
|
|
18
|
+
* is derived from the group's extensions.
|
|
19
|
+
*
|
|
20
|
+
* Grouping only surfaces when the FS Access picker is actually used (`useFsAccessApi` +
|
|
21
|
+
* a secure context + browser support). The native `<input>` fallback has no concept of groups
|
|
22
|
+
* or descriptions, so every group is flattened into one accept attribute there.
|
|
23
|
+
*/
|
|
24
|
+
interface AcceptGroup {
|
|
25
|
+
description?: string;
|
|
26
|
+
accept: Accept;
|
|
9
27
|
}
|
|
10
28
|
/**
|
|
11
29
|
* A file rejection error.
|
|
@@ -37,7 +55,7 @@ interface FileRejection {
|
|
|
37
55
|
}
|
|
38
56
|
type SharedProps = "multiple" | "onDragEnter" | "onDragOver" | "onDragLeave";
|
|
39
57
|
type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, SharedProps> & {
|
|
40
|
-
accept?: Accept;
|
|
58
|
+
accept?: Accept | AcceptGroup[];
|
|
41
59
|
minSize?: number;
|
|
42
60
|
maxSize?: number;
|
|
43
61
|
maxFiles?: number;
|
|
@@ -46,6 +64,14 @@ type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, SharedProps> & {
|
|
|
46
64
|
noKeyboard?: boolean;
|
|
47
65
|
noDrag?: boolean;
|
|
48
66
|
noDragEventsBubbling?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* If true, disables paste-to-upload. By default, when the dropzone (or a focused child) receives a
|
|
69
|
+
* paste that carries files - e.g. a screenshot pasted with Ctrl/Cmd+V - those files go through the
|
|
70
|
+
* same `accept`/size/`validator` checks and `onDrop` callbacks as a drop. Pastes with no files
|
|
71
|
+
* (plain text, etc.) are ignored and left untouched. See
|
|
72
|
+
* https://github.com/react-dropzone/react-dropzone/issues/1210
|
|
73
|
+
*/
|
|
74
|
+
noPaste?: boolean;
|
|
49
75
|
disabled?: boolean;
|
|
50
76
|
onDrop?: <T extends File>(acceptedFiles: T[], fileRejections: FileRejection[], event: DropEvent) => void;
|
|
51
77
|
onDropAccepted?: <T extends File>(files: T[], event: DropEvent) => void;
|
|
@@ -75,7 +101,7 @@ type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, SharedProps> & {
|
|
|
75
101
|
useFsAccessApi?: boolean;
|
|
76
102
|
autoFocus?: boolean;
|
|
77
103
|
};
|
|
78
|
-
type DropEvent = React.DragEvent<HTMLElement> | React.ChangeEvent<HTMLInputElement> | DragEvent | Event;
|
|
104
|
+
type DropEvent = React.DragEvent<HTMLElement> | React.ChangeEvent<HTMLInputElement> | React.ClipboardEvent<HTMLElement> | DragEvent | ClipboardEvent | Event;
|
|
79
105
|
interface DropzoneRef {
|
|
80
106
|
open: () => void;
|
|
81
107
|
}
|
|
@@ -145,5 +171,5 @@ declare const Dropzone: React.ForwardRefExoticComponent<DropzoneProps & React.Re
|
|
|
145
171
|
*/
|
|
146
172
|
declare function useDropzone(props?: DropzoneOptions): DropzoneState;
|
|
147
173
|
//#endregion
|
|
148
|
-
export { type Accept, DropEvent, DropzoneInputProps, DropzoneOptions, DropzoneProps, DropzoneRef, DropzoneRootProps, DropzoneState, ErrorCode, type FileError, FileRejection, type FileWithPath, type ValidatorResult, Dropzone as default, useDropzone };
|
|
174
|
+
export { type Accept, type AcceptGroup, DropEvent, DropzoneInputProps, DropzoneOptions, DropzoneProps, DropzoneRef, DropzoneRootProps, DropzoneState, ErrorCode, type FileError, FileRejection, type FileWithPath, type ValidatorResult, Dropzone as default, useDropzone };
|
|
149
175
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -3,9 +3,27 @@ import * as React from "react";
|
|
|
3
3
|
//#region src/utils/index.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* A map of accepted MIME types to file extensions, as passed to the `accept` prop.
|
|
6
|
+
*
|
|
7
|
+
* An extension value may be a single extension string or an array of them - both are
|
|
8
|
+
* accepted, mirroring the shape of `window.showOpenFilePicker`'s `accept`.
|
|
6
9
|
*/
|
|
7
10
|
interface Accept {
|
|
8
|
-
[key: string]: readonly string[];
|
|
11
|
+
[key: string]: string | readonly string[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A labeled group of accepted types, mirroring one entry of `window.showOpenFilePicker`'s
|
|
15
|
+
* `types` option. Passing the `accept` prop as an array of these lets the File System Access
|
|
16
|
+
* picker present multiple named filter rows instead of a single one (see
|
|
17
|
+
* {@link pickerOptionsFromAccept}). The optional `description` labels the row; when omitted it
|
|
18
|
+
* is derived from the group's extensions.
|
|
19
|
+
*
|
|
20
|
+
* Grouping only surfaces when the FS Access picker is actually used (`useFsAccessApi` +
|
|
21
|
+
* a secure context + browser support). The native `<input>` fallback has no concept of groups
|
|
22
|
+
* or descriptions, so every group is flattened into one accept attribute there.
|
|
23
|
+
*/
|
|
24
|
+
interface AcceptGroup {
|
|
25
|
+
description?: string;
|
|
26
|
+
accept: Accept;
|
|
9
27
|
}
|
|
10
28
|
/**
|
|
11
29
|
* A file rejection error.
|
|
@@ -37,7 +55,7 @@ interface FileRejection {
|
|
|
37
55
|
}
|
|
38
56
|
type SharedProps = "multiple" | "onDragEnter" | "onDragOver" | "onDragLeave";
|
|
39
57
|
type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, SharedProps> & {
|
|
40
|
-
accept?: Accept;
|
|
58
|
+
accept?: Accept | AcceptGroup[];
|
|
41
59
|
minSize?: number;
|
|
42
60
|
maxSize?: number;
|
|
43
61
|
maxFiles?: number;
|
|
@@ -46,6 +64,14 @@ type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, SharedProps> & {
|
|
|
46
64
|
noKeyboard?: boolean;
|
|
47
65
|
noDrag?: boolean;
|
|
48
66
|
noDragEventsBubbling?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* If true, disables paste-to-upload. By default, when the dropzone (or a focused child) receives a
|
|
69
|
+
* paste that carries files - e.g. a screenshot pasted with Ctrl/Cmd+V - those files go through the
|
|
70
|
+
* same `accept`/size/`validator` checks and `onDrop` callbacks as a drop. Pastes with no files
|
|
71
|
+
* (plain text, etc.) are ignored and left untouched. See
|
|
72
|
+
* https://github.com/react-dropzone/react-dropzone/issues/1210
|
|
73
|
+
*/
|
|
74
|
+
noPaste?: boolean;
|
|
49
75
|
disabled?: boolean;
|
|
50
76
|
onDrop?: <T extends File>(acceptedFiles: T[], fileRejections: FileRejection[], event: DropEvent) => void;
|
|
51
77
|
onDropAccepted?: <T extends File>(files: T[], event: DropEvent) => void;
|
|
@@ -75,7 +101,7 @@ type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, SharedProps> & {
|
|
|
75
101
|
useFsAccessApi?: boolean;
|
|
76
102
|
autoFocus?: boolean;
|
|
77
103
|
};
|
|
78
|
-
type DropEvent = React.DragEvent<HTMLElement> | React.ChangeEvent<HTMLInputElement> | DragEvent | Event;
|
|
104
|
+
type DropEvent = React.DragEvent<HTMLElement> | React.ChangeEvent<HTMLInputElement> | React.ClipboardEvent<HTMLElement> | DragEvent | ClipboardEvent | Event;
|
|
79
105
|
interface DropzoneRef {
|
|
80
106
|
open: () => void;
|
|
81
107
|
}
|
|
@@ -145,5 +171,5 @@ declare const Dropzone: React.ForwardRefExoticComponent<DropzoneProps & React.Re
|
|
|
145
171
|
*/
|
|
146
172
|
declare function useDropzone(props?: DropzoneOptions): DropzoneState;
|
|
147
173
|
//#endregion
|
|
148
|
-
export { type Accept, DropEvent, DropzoneInputProps, DropzoneOptions, DropzoneProps, DropzoneRef, DropzoneRootProps, DropzoneState, ErrorCode, type FileError, FileRejection, type FileWithPath, type ValidatorResult, Dropzone as default, useDropzone };
|
|
174
|
+
export { type Accept, type AcceptGroup, DropEvent, DropzoneInputProps, DropzoneOptions, DropzoneProps, DropzoneRef, DropzoneRootProps, DropzoneState, ErrorCode, type FileError, FileRejection, type FileWithPath, type ValidatorResult, Dropzone as default, useDropzone };
|
|
149
175
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -135,8 +135,9 @@ function isPropagationStopped(event) {
|
|
|
135
135
|
return false;
|
|
136
136
|
}
|
|
137
137
|
function isEvtWithFiles(event) {
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
const dataTransfer = event.dataTransfer ?? event.clipboardData;
|
|
139
|
+
if (!dataTransfer) return !!event.target && !!event.target.files;
|
|
140
|
+
return Array.prototype.some.call(dataTransfer.types, (type) => type === "Files" || type === "application/x-moz-file") || Array.prototype.some.call(dataTransfer.items ?? [], isKindFile);
|
|
140
141
|
}
|
|
141
142
|
function isKindFile(item) {
|
|
142
143
|
return typeof item === "object" && item !== null && item.kind === "file";
|
|
@@ -173,27 +174,91 @@ function canUseFileSystemAccessAPI() {
|
|
|
173
174
|
return "showOpenFilePicker" in window;
|
|
174
175
|
}
|
|
175
176
|
/**
|
|
177
|
+
* Coerce an `accept` extension value to an array. `window.showOpenFilePicker` allows a single
|
|
178
|
+
* extension string as well as an array, so we accept both and normalize to an array. Anything
|
|
179
|
+
* else (a malformed value) collapses to an empty list.
|
|
180
|
+
*/
|
|
181
|
+
function toExtensions(ext) {
|
|
182
|
+
if (Array.isArray(ext)) return ext;
|
|
183
|
+
if (typeof ext === "string") return [ext];
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Normalize either `accept` form to the internal array of `{description, accept}` groups.
|
|
188
|
+
*
|
|
189
|
+
* - The object form (`{mime: ext}`) becomes a single group; its `description` (which the object
|
|
190
|
+
* form can't express) is derived from the extensions like any other group.
|
|
191
|
+
* - The array form is passed through; entries missing an `accept` map are dropped.
|
|
192
|
+
*
|
|
193
|
+
* In both cases a missing `description` is filled in later from the group's extensions (see
|
|
194
|
+
* {@link describeAccept}), so the picker always gets the non-empty description it requires
|
|
195
|
+
* (https://crbug.com/1264708). Returns `undefined` when no `accept` is provided.
|
|
196
|
+
*/
|
|
197
|
+
function normalizeAcceptGroups(accept) {
|
|
198
|
+
if (!isDefined(accept)) return;
|
|
199
|
+
if (Array.isArray(accept)) return accept.filter((group) => isDefined(group) && isDefined(group.accept));
|
|
200
|
+
return [{ accept }];
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Build a human-readable label for a picker group from its (validated) accept map, used when the
|
|
204
|
+
* caller doesn't supply a `description`. Prefer the file extensions, fall back to the MIME types,
|
|
205
|
+
* then to a generic label - `showOpenFilePicker` requires a non-empty description (crbug 1264708).
|
|
206
|
+
*/
|
|
207
|
+
function describeAccept(accept) {
|
|
208
|
+
const extensions = [];
|
|
209
|
+
const mimeTypes = Object.keys(accept);
|
|
210
|
+
for (const ext of Object.values(accept)) for (const e of toExtensions(ext)) if (!extensions.includes(e)) extensions.push(e);
|
|
211
|
+
if (extensions.length > 0) return extensions.join(", ");
|
|
212
|
+
if (mimeTypes.length > 0) return mimeTypes.join(", ");
|
|
213
|
+
return "Files";
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Merge every `accept` group into a single MIME-type -> extensions map. Duplicate MIME keys union
|
|
217
|
+
* their extensions. This flattened map drives the native `<input accept>` attribute and the
|
|
218
|
+
* drag/drop validators, which have no concept of groups or descriptions.
|
|
219
|
+
*/
|
|
220
|
+
function flattenAccept(accept) {
|
|
221
|
+
const groups = normalizeAcceptGroups(accept);
|
|
222
|
+
if (!isDefined(groups)) return;
|
|
223
|
+
const merged = {};
|
|
224
|
+
for (const group of groups) for (const [mimeType, ext] of Object.entries(group.accept)) {
|
|
225
|
+
const existing = merged[mimeType] ?? (merged[mimeType] = []);
|
|
226
|
+
for (const e of toExtensions(ext)) if (!existing.includes(e)) existing.push(e);
|
|
227
|
+
}
|
|
228
|
+
return merged;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
176
231
|
* Convert the `{accept}` dropzone prop to the `{types}` option for showOpenFilePicker.
|
|
232
|
+
*
|
|
233
|
+
* Each group becomes one filter row in the picker. Invalid MIME types / extensions are dropped
|
|
234
|
+
* with a warning; a group left with no valid entries is omitted entirely (showOpenFilePicker
|
|
235
|
+
* rejects an empty `accept`). Returns `undefined` when nothing valid remains.
|
|
177
236
|
*/
|
|
178
237
|
function pickerOptionsFromAccept(accept) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
238
|
+
const groups = normalizeAcceptGroups(accept);
|
|
239
|
+
if (!isDefined(groups)) return;
|
|
240
|
+
const options = groups.map((group) => {
|
|
241
|
+
const acceptForPicker = Object.entries(group.accept).filter(([mimeType, ext]) => {
|
|
182
242
|
let ok = true;
|
|
183
243
|
if (!isMIMEType(mimeType)) {
|
|
184
244
|
console.warn(`Skipped "${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.`);
|
|
185
245
|
ok = false;
|
|
186
246
|
}
|
|
187
|
-
if (!Array.isArray(ext) || !ext.every(isExt)) {
|
|
247
|
+
if (!(Array.isArray(ext) || typeof ext === "string") || !toExtensions(ext).every(isExt)) {
|
|
188
248
|
console.warn(`Skipped "${mimeType}" because an invalid file extension was provided.`);
|
|
189
249
|
ok = false;
|
|
190
250
|
}
|
|
191
251
|
return ok;
|
|
192
252
|
}).reduce((agg, [mimeType, ext]) => {
|
|
193
|
-
agg[mimeType] = ext;
|
|
253
|
+
agg[mimeType] = toExtensions(ext);
|
|
194
254
|
return agg;
|
|
195
|
-
}, {})
|
|
196
|
-
|
|
255
|
+
}, {});
|
|
256
|
+
return {
|
|
257
|
+
description: isDefined(group.description) && group.description !== "" ? group.description : describeAccept(acceptForPicker),
|
|
258
|
+
accept: acceptForPicker
|
|
259
|
+
};
|
|
260
|
+
}).filter((option) => Object.keys(option.accept).length > 0);
|
|
261
|
+
return options.length > 0 ? options : void 0;
|
|
197
262
|
}
|
|
198
263
|
/**
|
|
199
264
|
* Convert the `{accept}` dropzone prop to a comma-separated accept attribute string.
|
|
@@ -208,7 +273,8 @@ function pickerOptionsFromAccept(accept) {
|
|
|
208
273
|
* See https://github.com/react-dropzone/react-dropzone/issues/1220
|
|
209
274
|
*/
|
|
210
275
|
function acceptPropAsAcceptAttr(accept, { omitWildcardMimeTypesWithExtensions = false } = {}) {
|
|
211
|
-
if (isDefined(accept)) return Object.entries(accept).reduce((a, [mimeType,
|
|
276
|
+
if (isDefined(accept)) return Object.entries(accept).reduce((a, [mimeType, extVal]) => {
|
|
277
|
+
const ext = toExtensions(extVal);
|
|
212
278
|
if (omitWildcardMimeTypesWithExtensions && isMIMETypeWildcard(mimeType) && ext.some(isExt)) a.push(...ext);
|
|
213
279
|
else a.push(mimeType, ...ext);
|
|
214
280
|
return a;
|
|
@@ -312,9 +378,10 @@ const initialState = {
|
|
|
312
378
|
* ```
|
|
313
379
|
*/
|
|
314
380
|
function useDropzone(props = {}) {
|
|
315
|
-
const { accept, disabled = false, getFilesFromEvent = fromEvent, maxSize = Number.POSITIVE_INFINITY, minSize = 0, multiple = true, maxFiles = 0, onDragEnter, onDragLeave, onDragOver, onDrop, onDropAccepted, onDropRejected, onFileDialogCancel, onFileDialogOpen, useFsAccessApi = false, autoFocus = false, preventDropOnDocument = true, noClick = false, noKeyboard = false, noDrag = false, noDragEventsBubbling = false, onError, validator, getErrorMessage } = props;
|
|
316
|
-
const
|
|
317
|
-
const
|
|
381
|
+
const { accept, disabled = false, getFilesFromEvent = fromEvent, maxSize = Number.POSITIVE_INFINITY, minSize = 0, multiple = true, maxFiles = 0, onDragEnter, onDragLeave, onDragOver, onDrop, onDropAccepted, onDropRejected, onFileDialogCancel, onFileDialogOpen, useFsAccessApi = false, autoFocus = false, preventDropOnDocument = true, noClick = false, noKeyboard = false, noDrag = false, noDragEventsBubbling = false, noPaste = false, onError, validator, getErrorMessage } = props;
|
|
382
|
+
const flatAccept = useMemo(() => flattenAccept(accept), [accept]);
|
|
383
|
+
const acceptAttr = useMemo(() => acceptPropAsAcceptAttr(flatAccept), [flatAccept]);
|
|
384
|
+
const inputAcceptAttr = useMemo(() => acceptPropAsAcceptAttr(flatAccept, { omitWildcardMimeTypesWithExtensions: true }), [flatAccept]);
|
|
318
385
|
const pickerTypes = useMemo(() => pickerOptionsFromAccept(accept), [accept]);
|
|
319
386
|
const onFileDialogOpenCb = useMemo(() => typeof onFileDialogOpen === "function" ? onFileDialogOpen : noop, [onFileDialogOpen]);
|
|
320
387
|
const onFileDialogCancelCb = useMemo(() => typeof onFileDialogCancel === "function" ? onFileDialogCancel : noop, [onFileDialogCancel]);
|
|
@@ -620,6 +687,33 @@ function useDropzone(props = {}) {
|
|
|
620
687
|
beginProcessing,
|
|
621
688
|
endProcessing
|
|
622
689
|
]);
|
|
690
|
+
const onPasteCb = useCallback((event) => {
|
|
691
|
+
if (!isEvtWithFiles(event)) return;
|
|
692
|
+
event.preventDefault();
|
|
693
|
+
event.persist?.();
|
|
694
|
+
stopPropagation(event);
|
|
695
|
+
const signal = beginProcessing();
|
|
696
|
+
Promise.resolve(getFilesFromEvent(event)).then((files) => {
|
|
697
|
+
if (signal.aborted) return;
|
|
698
|
+
if (isPropagationStopped(event) && !noDragEventsBubbling) {
|
|
699
|
+
endProcessing(signal);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
return setFiles(files, event, signal);
|
|
703
|
+
}).catch((e) => {
|
|
704
|
+
if (!signal.aborted) {
|
|
705
|
+
endProcessing(signal);
|
|
706
|
+
onErrCb(e);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
}, [
|
|
710
|
+
getFilesFromEvent,
|
|
711
|
+
setFiles,
|
|
712
|
+
onErrCb,
|
|
713
|
+
noDragEventsBubbling,
|
|
714
|
+
beginProcessing,
|
|
715
|
+
endProcessing
|
|
716
|
+
]);
|
|
623
717
|
const openFileDialog = useCallback(() => {
|
|
624
718
|
if (fsAccessApiWorksRef.current) {
|
|
625
719
|
dispatch({ type: "openDialog" });
|
|
@@ -696,10 +790,13 @@ function useDropzone(props = {}) {
|
|
|
696
790
|
const composeDragHandler = (fn) => {
|
|
697
791
|
return noDrag ? null : composeHandler(fn);
|
|
698
792
|
};
|
|
793
|
+
const composePasteHandler = (fn) => {
|
|
794
|
+
return noPaste ? null : composeHandler(fn);
|
|
795
|
+
};
|
|
699
796
|
const stopPropagation = (event) => {
|
|
700
797
|
if (noDragEventsBubbling) event.stopPropagation();
|
|
701
798
|
};
|
|
702
|
-
const getRootProps = useMemo(() => ({ refKey = "ref", role, onKeyDown, onFocus, onBlur, onClick, onDragEnter, onDragOver, onDragLeave, onDrop, ...rest } = {}) => ({
|
|
799
|
+
const getRootProps = useMemo(() => ({ refKey = "ref", role, onKeyDown, onFocus, onBlur, onClick, onDragEnter, onDragOver, onDragLeave, onDrop, onPaste, ...rest } = {}) => ({
|
|
703
800
|
onKeyDown: composeKeyboardHandler(composeEventHandlers(onKeyDown, onKeyDownCb)),
|
|
704
801
|
onFocus: composeKeyboardHandler(composeEventHandlers(onFocus, onFocusCb)),
|
|
705
802
|
onBlur: composeKeyboardHandler(composeEventHandlers(onBlur, onBlurCb)),
|
|
@@ -708,6 +805,7 @@ function useDropzone(props = {}) {
|
|
|
708
805
|
onDragOver: composeDragHandler(composeEventHandlers(onDragOver, onDragOverCb)),
|
|
709
806
|
onDragLeave: composeDragHandler(composeEventHandlers(onDragLeave, onDragLeaveCb)),
|
|
710
807
|
onDrop: composeDragHandler(composeEventHandlers(onDrop, onDropCb)),
|
|
808
|
+
onPaste: composePasteHandler(composeEventHandlers(onPaste, onPasteCb)),
|
|
711
809
|
role: typeof role === "string" && role !== "" ? role : "presentation",
|
|
712
810
|
[refKey]: rootRef,
|
|
713
811
|
...!disabled && !noKeyboard ? { tabIndex: 0 } : {},
|
|
@@ -723,8 +821,10 @@ function useDropzone(props = {}) {
|
|
|
723
821
|
onDragOverCb,
|
|
724
822
|
onDragLeaveCb,
|
|
725
823
|
onDropCb,
|
|
824
|
+
onPasteCb,
|
|
726
825
|
noKeyboard,
|
|
727
826
|
noDrag,
|
|
827
|
+
noPaste,
|
|
728
828
|
disabled
|
|
729
829
|
]);
|
|
730
830
|
const onInputElementClick = useCallback((event) => {
|