react-dropzone 16.0.0 → 18.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.
@@ -1,362 +0,0 @@
1
- import _accepts from "attr-accept";
2
-
3
- const accepts = typeof _accepts === "function" ? _accepts : _accepts.default;
4
-
5
- // Error codes
6
- export const FILE_INVALID_TYPE = "file-invalid-type";
7
- export const FILE_TOO_LARGE = "file-too-large";
8
- export const FILE_TOO_SMALL = "file-too-small";
9
- export const TOO_MANY_FILES = "too-many-files";
10
-
11
- export const ErrorCode = {
12
- FileInvalidType: FILE_INVALID_TYPE,
13
- FileTooLarge: FILE_TOO_LARGE,
14
- FileTooSmall: FILE_TOO_SMALL,
15
- TooManyFiles: TOO_MANY_FILES,
16
- };
17
-
18
- /**
19
- *
20
- * @param {string} accept
21
- */
22
- export const getInvalidTypeRejectionErr = (accept = "") => {
23
- const acceptArr = accept.split(",");
24
- const msg =
25
- acceptArr.length > 1 ? `one of ${acceptArr.join(", ")}` : acceptArr[0];
26
-
27
- return {
28
- code: FILE_INVALID_TYPE,
29
- message: `File type must be ${msg}`,
30
- };
31
- };
32
-
33
- export const getTooLargeRejectionErr = (maxSize) => {
34
- return {
35
- code: FILE_TOO_LARGE,
36
- message: `File is larger than ${maxSize} ${
37
- maxSize === 1 ? "byte" : "bytes"
38
- }`,
39
- };
40
- };
41
-
42
- export const getTooSmallRejectionErr = (minSize) => {
43
- return {
44
- code: FILE_TOO_SMALL,
45
- message: `File is smaller than ${minSize} ${
46
- minSize === 1 ? "byte" : "bytes"
47
- }`,
48
- };
49
- };
50
-
51
- export const TOO_MANY_FILES_REJECTION = {
52
- code: TOO_MANY_FILES,
53
- message: "Too many files",
54
- };
55
-
56
- /**
57
- * Check if the given file is a DataTransferItem with an empty type.
58
- *
59
- * During drag events, browsers may return DataTransferItem objects instead of File objects.
60
- * Some browsers (e.g., Chrome) return an empty MIME type for certain file types (like .md files)
61
- * on DataTransferItem during drag events, even though the type is correctly set during drop.
62
- *
63
- * This function detects such cases by checking for:
64
- * 1. Empty type string
65
- * 2. Presence of getAsFile method (indicates it's a DataTransferItem, not a File)
66
- *
67
- * We accept these during drag to provide proper UI feedback, while maintaining
68
- * strict validation during drop when real File objects are available.
69
- *
70
- * @param {File | DataTransferItem} file
71
- * @returns {boolean}
72
- */
73
- export function isDataTransferItemWithEmptyType(file) {
74
- return file.type === "" && typeof file.getAsFile === "function";
75
- }
76
-
77
- /**
78
- * Check if file is accepted.
79
- *
80
- * Firefox versions prior to 53 return a bogus MIME type for every file drag,
81
- * so dragovers with that MIME type will always be accepted.
82
- *
83
- * Chrome/other browsers may return an empty MIME type for files during drag events,
84
- * so we accept those as well (we'll validate properly on drop).
85
- *
86
- * @param {File} file
87
- * @param {string} accept
88
- * @returns
89
- */
90
- export function fileAccepted(file, accept) {
91
- const isAcceptable =
92
- file.type === "application/x-moz-file" ||
93
- accepts(file, accept) ||
94
- isDataTransferItemWithEmptyType(file);
95
- return [
96
- isAcceptable,
97
- isAcceptable ? null : getInvalidTypeRejectionErr(accept),
98
- ];
99
- }
100
-
101
- export function fileMatchSize(file, minSize, maxSize) {
102
- if (isDefined(file.size)) {
103
- if (isDefined(minSize) && isDefined(maxSize)) {
104
- if (file.size > maxSize) return [false, getTooLargeRejectionErr(maxSize)];
105
- if (file.size < minSize) return [false, getTooSmallRejectionErr(minSize)];
106
- } else if (isDefined(minSize) && file.size < minSize)
107
- return [false, getTooSmallRejectionErr(minSize)];
108
- else if (isDefined(maxSize) && file.size > maxSize)
109
- return [false, getTooLargeRejectionErr(maxSize)];
110
- }
111
- return [true, null];
112
- }
113
-
114
- function isDefined(value) {
115
- return value !== undefined && value !== null;
116
- }
117
-
118
- /**
119
- *
120
- * @param {object} options
121
- * @param {File[]} options.files
122
- * @param {string} [options.accept]
123
- * @param {number} [options.minSize]
124
- * @param {number} [options.maxSize]
125
- * @param {boolean} [options.multiple]
126
- * @param {number} [options.maxFiles]
127
- * @param {(f: File) => FileError|FileError[]|null} [options.validator]
128
- * @returns
129
- */
130
- export function allFilesAccepted({
131
- files,
132
- accept,
133
- minSize,
134
- maxSize,
135
- multiple,
136
- maxFiles,
137
- validator,
138
- }) {
139
- if (
140
- (!multiple && files.length > 1) ||
141
- (multiple && maxFiles >= 1 && files.length > maxFiles)
142
- ) {
143
- return false;
144
- }
145
-
146
- return files.every((file) => {
147
- const [accepted] = fileAccepted(file, accept);
148
- const [sizeMatch] = fileMatchSize(file, minSize, maxSize);
149
- const customErrors = validator ? validator(file) : null;
150
- return accepted && sizeMatch && !customErrors;
151
- });
152
- }
153
-
154
- // React's synthetic events has event.isPropagationStopped,
155
- // but to remain compatibility with other libs (Preact) fall back
156
- // to check event.cancelBubble
157
- export function isPropagationStopped(event) {
158
- if (typeof event.isPropagationStopped === "function") {
159
- return event.isPropagationStopped();
160
- } else if (typeof event.cancelBubble !== "undefined") {
161
- return event.cancelBubble;
162
- }
163
- return false;
164
- }
165
-
166
- export function isEvtWithFiles(event) {
167
- if (!event.dataTransfer) {
168
- return !!event.target && !!event.target.files;
169
- }
170
- // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
171
- // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
172
- return Array.prototype.some.call(
173
- event.dataTransfer.types,
174
- (type) => type === "Files" || type === "application/x-moz-file"
175
- );
176
- }
177
-
178
- export function isKindFile(item) {
179
- return typeof item === "object" && item !== null && item.kind === "file";
180
- }
181
-
182
- // allow the entire document to be a drag target
183
- export function onDocumentDragOver(event) {
184
- event.preventDefault();
185
- }
186
-
187
- function isIe(userAgent) {
188
- return (
189
- userAgent.indexOf("MSIE") !== -1 || userAgent.indexOf("Trident/") !== -1
190
- );
191
- }
192
-
193
- function isEdge(userAgent) {
194
- return userAgent.indexOf("Edge/") !== -1;
195
- }
196
-
197
- export function isIeOrEdge(userAgent = window.navigator.userAgent) {
198
- return isIe(userAgent) || isEdge(userAgent);
199
- }
200
-
201
- /**
202
- * This is intended to be used to compose event handlers
203
- * They are executed in order until one of them calls `event.isPropagationStopped()`.
204
- * Note that the check is done on the first invoke too,
205
- * meaning that if propagation was stopped before invoking the fns,
206
- * no handlers will be executed.
207
- *
208
- * @param {Function} fns the event hanlder functions
209
- * @return {Function} the event handler to add to an element
210
- */
211
- export function composeEventHandlers(...fns) {
212
- return (event, ...args) =>
213
- fns.some((fn) => {
214
- if (!isPropagationStopped(event) && fn) {
215
- fn(event, ...args);
216
- }
217
- return isPropagationStopped(event);
218
- });
219
- }
220
-
221
- /**
222
- * canUseFileSystemAccessAPI checks if the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API)
223
- * is supported by the browser.
224
- * @returns {boolean}
225
- */
226
- export function canUseFileSystemAccessAPI() {
227
- return "showOpenFilePicker" in window;
228
- }
229
-
230
- /**
231
- * Convert the `{accept}` dropzone prop to the
232
- * `{types}` option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
233
- *
234
- * @param {AcceptProp} accept
235
- * @returns {{accept: string[]}[]}
236
- */
237
- export function pickerOptionsFromAccept(accept) {
238
- if (isDefined(accept)) {
239
- const acceptForPicker = Object.entries(accept)
240
- .filter(([mimeType, ext]) => {
241
- let ok = true;
242
-
243
- if (!isMIMEType(mimeType)) {
244
- console.warn(
245
- `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.`
246
- );
247
- ok = false;
248
- }
249
-
250
- if (!Array.isArray(ext) || !ext.every(isExt)) {
251
- console.warn(
252
- `Skipped "${mimeType}" because an invalid file extension was provided.`
253
- );
254
- ok = false;
255
- }
256
-
257
- return ok;
258
- })
259
- .reduce(
260
- (agg, [mimeType, ext]) => ({
261
- ...agg,
262
- [mimeType]: ext,
263
- }),
264
- {}
265
- );
266
- return [
267
- {
268
- // description is required due to https://crbug.com/1264708
269
- description: "Files",
270
- accept: acceptForPicker,
271
- },
272
- ];
273
- }
274
- return accept;
275
- }
276
-
277
- /**
278
- * Convert the `{accept}` dropzone prop to an array of MIME types/extensions.
279
- * @param {AcceptProp} accept
280
- * @returns {string}
281
- */
282
- export function acceptPropAsAcceptAttr(accept) {
283
- if (isDefined(accept)) {
284
- return (
285
- Object.entries(accept)
286
- .reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], [])
287
- // Silently discard invalid entries as pickerOptionsFromAccept warns about these
288
- .filter((v) => isMIMEType(v) || isExt(v))
289
- .join(",")
290
- );
291
- }
292
-
293
- return undefined;
294
- }
295
-
296
- /**
297
- * Check if v is an exception caused by aborting a request (e.g window.showOpenFilePicker()).
298
- *
299
- * See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
300
- * @param {any} v
301
- * @returns {boolean} True if v is an abort exception.
302
- */
303
- export function isAbort(v) {
304
- return (
305
- v instanceof DOMException &&
306
- (v.name === "AbortError" || v.code === v.ABORT_ERR)
307
- );
308
- }
309
-
310
- /**
311
- * Check if v is a security error.
312
- *
313
- * See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
314
- * @param {any} v
315
- * @returns {boolean} True if v is a security error.
316
- */
317
- export function isSecurityError(v) {
318
- return (
319
- v instanceof DOMException &&
320
- (v.name === "SecurityError" || v.code === v.SECURITY_ERR)
321
- );
322
- }
323
-
324
- /**
325
- * Check if v is a MIME type string.
326
- *
327
- * See accepted format: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers.
328
- *
329
- * @param {string} v
330
- */
331
- export function isMIMEType(v) {
332
- return (
333
- v === "audio/*" ||
334
- v === "video/*" ||
335
- v === "image/*" ||
336
- v === "text/*" ||
337
- v === "application/*" ||
338
- /\w+\/[-+.\w]+/g.test(v)
339
- );
340
- }
341
-
342
- /**
343
- * Check if v is a file extension.
344
- * @param {string} v
345
- */
346
- export function isExt(v) {
347
- return /^.*\.[\w]+$/.test(v);
348
- }
349
-
350
- /**
351
- * @typedef {Object.<string, string[]>} AcceptProp
352
- */
353
-
354
- /**
355
- * @typedef {object} FileError
356
- * @property {string} message
357
- * @property {ErrorCode|string} code
358
- */
359
-
360
- /**
361
- * @typedef {"file-invalid-type"|"file-too-large"|"file-too-small"|"too-many-files"} ErrorCode
362
- */
@@ -1,102 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { FileWithPath } from "file-selector";
4
- export { FileWithPath };
5
- export default function Dropzone(
6
- props: DropzoneProps & React.RefAttributes<DropzoneRef>
7
- ): React.ReactElement;
8
- export function useDropzone(options?: DropzoneOptions): DropzoneState;
9
-
10
- export interface DropzoneProps extends DropzoneOptions {
11
- children?(state: DropzoneState): React.ReactElement;
12
- }
13
-
14
- export enum ErrorCode {
15
- FileInvalidType = "file-invalid-type",
16
- FileTooLarge = "file-too-large",
17
- FileTooSmall = "file-too-small",
18
- TooManyFiles = "too-many-files",
19
- }
20
-
21
- export interface FileError {
22
- message: string;
23
- code: ErrorCode | string;
24
- }
25
-
26
- export interface FileRejection {
27
- file: FileWithPath;
28
- errors: readonly FileError[];
29
- }
30
-
31
- export type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, PropTypes> & {
32
- accept?: Accept;
33
- minSize?: number;
34
- maxSize?: number;
35
- maxFiles?: number;
36
- preventDropOnDocument?: boolean;
37
- noClick?: boolean;
38
- noKeyboard?: boolean;
39
- noDrag?: boolean;
40
- noDragEventsBubbling?: boolean;
41
- disabled?: boolean;
42
- onDrop?: <T extends File>(
43
- acceptedFiles: T[],
44
- fileRejections: FileRejection[],
45
- event: DropEvent
46
- ) => void;
47
- onDropAccepted?: <T extends File>(files: T[], event: DropEvent) => void;
48
- onDropRejected?: (fileRejections: FileRejection[], event: DropEvent) => void;
49
- getFilesFromEvent?: (
50
- event: DropEvent
51
- ) => Promise<Array<File | DataTransferItem>>;
52
- onFileDialogCancel?: () => void;
53
- onFileDialogOpen?: () => void;
54
- onError?: (err: Error) => void;
55
- validator?: <T extends File>(
56
- file: T
57
- ) => FileError | readonly FileError[] | null;
58
- useFsAccessApi?: boolean;
59
- autoFocus?: boolean;
60
- };
61
-
62
- export type DropEvent =
63
- | React.DragEvent<HTMLElement>
64
- | React.ChangeEvent<HTMLInputElement>
65
- | DragEvent
66
- | Event
67
- | Array<FileSystemFileHandle>;
68
-
69
- export type DropzoneState = DropzoneRef & {
70
- isFocused: boolean;
71
- isDragActive: boolean;
72
- isDragAccept: boolean;
73
- isDragReject: boolean;
74
- isDragGlobal: boolean;
75
- isFileDialogActive: boolean;
76
- acceptedFiles: readonly FileWithPath[];
77
- fileRejections: readonly FileRejection[];
78
- rootRef: React.RefObject<HTMLElement>;
79
- inputRef: React.RefObject<HTMLInputElement>;
80
- getRootProps: <T extends DropzoneRootProps>(props?: T) => T;
81
- getInputProps: <T extends DropzoneInputProps>(props?: T) => T;
82
- };
83
-
84
- export interface DropzoneRef {
85
- open: () => void;
86
- }
87
-
88
- export interface DropzoneRootProps extends React.HTMLAttributes<HTMLElement> {
89
- refKey?: string;
90
- [key: string]: any;
91
- }
92
-
93
- export interface DropzoneInputProps
94
- extends React.InputHTMLAttributes<HTMLInputElement> {
95
- refKey?: string;
96
- }
97
-
98
- type PropTypes = "multiple" | "onDragEnter" | "onDragOver" | "onDragLeave";
99
-
100
- export interface Accept {
101
- [key: string]: readonly string[];
102
- }
@@ -1,54 +0,0 @@
1
- import React from "react";
2
- import Dropzone from "../../";
3
-
4
- export default class Accept extends React.Component {
5
- state = {
6
- accepted: [],
7
- rejected: [],
8
- };
9
-
10
- render() {
11
- return (
12
- <section>
13
- <div className="dropzone">
14
- <Dropzone
15
- accept={{
16
- "image/*": [".jpeg", ".png"],
17
- }}
18
- onDrop={(accepted, rejected) => {
19
- this.setState({ accepted, rejected });
20
- }}
21
- >
22
- {({ getRootProps }) => (
23
- <div {...getRootProps()}>
24
- <p>
25
- Try dropping some files here, or click to select files to
26
- upload.
27
- </p>
28
- <p>Only *.jpeg and *.png images will be accepted</p>
29
- </div>
30
- )}
31
- </Dropzone>
32
- </div>
33
- <aside>
34
- <h2>Accepted files</h2>
35
- <ul>
36
- {this.state.accepted.map((f) => (
37
- <li key={f.name}>
38
- {f.name} - {f.size} bytes
39
- </li>
40
- ))}
41
- </ul>
42
- <h2>Rejected files</h2>
43
- <ul>
44
- {this.state.rejected.map((f) => (
45
- <li key={f.name}>
46
- {f.name} - {f.size} bytes
47
- </li>
48
- ))}
49
- </ul>
50
- </aside>
51
- </section>
52
- );
53
- }
54
- }
@@ -1,46 +0,0 @@
1
- import React from "react";
2
- import Dropzone from "../../";
3
-
4
- export default class Test extends React.Component {
5
- render() {
6
- return (
7
- <div>
8
- <Dropzone
9
- onDrop={(acceptedFiles, fileRejections, event) =>
10
- console.log(acceptedFiles, fileRejections, event)
11
- }
12
- onDragEnter={(event) => console.log(event)}
13
- onDragOver={(event) => console.log(event)}
14
- onDragLeave={(event) => console.log(event)}
15
- onDropAccepted={(files, event) => console.log(files, event)}
16
- onDropRejected={(files, event) => console.log(files, event)}
17
- onFileDialogCancel={() => console.log("onFileDialogCancel invoked")}
18
- onFileDialogOpen={() => console.log("onFileDialogOpen invoked")}
19
- onError={(e) => console.log(e)}
20
- validator={(f) => ({ message: f.name, code: "" })}
21
- minSize={2000}
22
- maxSize={Infinity}
23
- maxFiles={100}
24
- preventDropOnDocument
25
- noClick={false}
26
- noKeyboard={false}
27
- noDrag={false}
28
- noDragEventsBubbling={false}
29
- disabled
30
- multiple={false}
31
- accept={{
32
- "image/*": [".png"],
33
- }}
34
- useFsAccessApi={false}
35
- autoFocus
36
- >
37
- {({ getRootProps, getInputProps }) => (
38
- <div {...getRootProps()}>
39
- <input {...getInputProps()} />
40
- </div>
41
- )}
42
- </Dropzone>
43
- </div>
44
- );
45
- }
46
- }
@@ -1,53 +0,0 @@
1
- import React from "react";
2
- import Dropzone from "../../";
3
- import { FileWithPath } from "file-selector";
4
-
5
- export default class Basic extends React.Component {
6
- state = { files: [] };
7
-
8
- onDrop = (files: FileWithPath[]) => {
9
- this.setState({
10
- files,
11
- });
12
- };
13
-
14
- render() {
15
- return (
16
- <section>
17
- <div className="dropzone">
18
- <Dropzone onDrop={this.onDrop}>
19
- {({ getRootProps, getInputProps }) => (
20
- <div {...getRootProps()}>
21
- <input {...getInputProps()} />
22
- <p>
23
- Try dropping some files here, or click to select files to
24
- upload.
25
- </p>
26
- </div>
27
- )}
28
- </Dropzone>
29
- </div>
30
- <aside>
31
- <h2>Dropped files</h2>
32
- <ul>
33
- {this.state.files.map((f) => (
34
- <li key={f.name}>
35
- {f.name} - {f.size} bytes
36
- </li>
37
- ))}
38
- </ul>
39
- </aside>
40
- </section>
41
- );
42
- }
43
- }
44
-
45
- export const optional = (
46
- <Dropzone>
47
- {({ getRootProps, getInputProps }) => (
48
- <div {...getRootProps()}>
49
- <input {...getInputProps()} />
50
- </div>
51
- )}
52
- </Dropzone>
53
- );
@@ -1,31 +0,0 @@
1
- import React from "react";
2
- import Dropzone from "../../";
3
-
4
- export class Events extends React.Component {
5
- render() {
6
- return (
7
- <section>
8
- <div className="dropzone">
9
- <Dropzone
10
- onDrop={(acceptedFiles, fileRejections, event) =>
11
- console.log(acceptedFiles, fileRejections, event)
12
- }
13
- onDragEnter={(event) => console.log(event)}
14
- onDragOver={(event) => console.log(event)}
15
- onDragLeave={(event) => console.log(event)}
16
- >
17
- {({ getRootProps, getInputProps }) => (
18
- <div {...getRootProps()}>
19
- <input {...getInputProps()} />
20
- <p>
21
- Try dropping some files here, or click to select files to
22
- upload.
23
- </p>
24
- </div>
25
- )}
26
- </Dropzone>
27
- </div>
28
- </section>
29
- );
30
- }
31
- }
@@ -1,20 +0,0 @@
1
- import React from "react";
2
- import Dropzone from "../../";
3
-
4
- export const dropzone = (
5
- <Dropzone
6
- onDrop={(files) => console.log(files)}
7
- onFileDialogCancel={() => console.log("onFileDialogCancel invoked")}
8
- onFileDialogOpen={() => console.log("onFileDialogOpen invoked")}
9
- >
10
- {({ getRootProps, getInputProps, open }) => (
11
- <div {...getRootProps()}>
12
- <input {...getInputProps()} />
13
- <p>Drop some files here.</p>
14
- <button type="button" onClick={open}>
15
- Open file dialog
16
- </button>
17
- </div>
18
- )}
19
- </Dropzone>
20
- );
@@ -1,15 +0,0 @@
1
- import React from "react";
2
- import { useDropzone, DropzoneProps } from "../../";
3
-
4
- export const Dropzone = ({ children, ...opts }: DropzoneProps) => {
5
- const { ...state } = useDropzone(opts);
6
- return children(state);
7
- };
8
-
9
- <Dropzone>
10
- {({ getRootProps, getInputProps }) => (
11
- <div {...getRootProps()}>
12
- <input {...getInputProps()} />
13
- </div>
14
- )}
15
- </Dropzone>;