react-dropzone 16.0.0 → 17.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/dist/index.js CHANGED
@@ -1,44 +1,40 @@
1
- import { Fragment, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef } from "react";
2
- import PropTypes from "prop-types";
3
1
  import { fromEvent } from "file-selector";
4
- import _accepts from "attr-accept";
5
- import { jsx } from "react/jsx-runtime";
6
- //#region src/utils/index.js
7
- const accepts = typeof _accepts === "function" ? _accepts : _accepts.default;
2
+ import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef } from "react";
3
+ import attrAccept from "attr-accept";
4
+ import { Fragment, jsx } from "react/jsx-runtime";
5
+ //#region src/utils/index.ts
6
+ const accepts = typeof attrAccept === "function" ? attrAccept : attrAccept.default;
8
7
  const FILE_INVALID_TYPE = "file-invalid-type";
9
8
  const FILE_TOO_LARGE = "file-too-large";
10
9
  const FILE_TOO_SMALL = "file-too-small";
11
10
  const TOO_MANY_FILES = "too-many-files";
12
- const ErrorCode = {
13
- FileInvalidType: FILE_INVALID_TYPE,
14
- FileTooLarge: FILE_TOO_LARGE,
15
- FileTooSmall: FILE_TOO_SMALL,
16
- TooManyFiles: TOO_MANY_FILES
17
- };
18
- /**
19
- *
20
- * @param {string} accept
21
- */
22
- const getInvalidTypeRejectionErr = (accept = "") => {
11
+ let ErrorCode = /* @__PURE__ */ function(ErrorCode) {
12
+ ErrorCode["FileInvalidType"] = "file-invalid-type";
13
+ ErrorCode["FileTooLarge"] = "file-too-large";
14
+ ErrorCode["FileTooSmall"] = "file-too-small";
15
+ ErrorCode["TooManyFiles"] = "too-many-files";
16
+ return ErrorCode;
17
+ }({});
18
+ function getInvalidTypeRejectionErr(accept = "") {
23
19
  const acceptArr = accept.split(",");
24
20
  const msg = acceptArr.length > 1 ? `one of ${acceptArr.join(", ")}` : acceptArr[0];
25
21
  return {
26
22
  code: FILE_INVALID_TYPE,
27
23
  message: `File type must be ${msg}`
28
24
  };
29
- };
30
- const getTooLargeRejectionErr = (maxSize) => {
25
+ }
26
+ function getTooLargeRejectionErr(maxSize) {
31
27
  return {
32
28
  code: FILE_TOO_LARGE,
33
29
  message: `File is larger than ${maxSize} ${maxSize === 1 ? "byte" : "bytes"}`
34
30
  };
35
- };
36
- const getTooSmallRejectionErr = (minSize) => {
31
+ }
32
+ function getTooSmallRejectionErr(minSize) {
37
33
  return {
38
34
  code: FILE_TOO_SMALL,
39
35
  message: `File is smaller than ${minSize} ${minSize === 1 ? "byte" : "bytes"}`
40
36
  };
41
- };
37
+ }
42
38
  const TOO_MANY_FILES_REJECTION = {
43
39
  code: TOO_MANY_FILES,
44
40
  message: "Too many files"
@@ -49,16 +45,6 @@ const TOO_MANY_FILES_REJECTION = {
49
45
  * During drag events, browsers may return DataTransferItem objects instead of File objects.
50
46
  * Some browsers (e.g., Chrome) return an empty MIME type for certain file types (like .md files)
51
47
  * on DataTransferItem during drag events, even though the type is correctly set during drop.
52
- *
53
- * This function detects such cases by checking for:
54
- * 1. Empty type string
55
- * 2. Presence of getAsFile method (indicates it's a DataTransferItem, not a File)
56
- *
57
- * We accept these during drag to provide proper UI feedback, while maintaining
58
- * strict validation during drop when real File objects are available.
59
- *
60
- * @param {File | DataTransferItem} file
61
- * @returns {boolean}
62
48
  */
63
49
  function isDataTransferItemWithEmptyType(file) {
64
50
  return file.type === "" && typeof file.getAsFile === "function";
@@ -71,13 +57,9 @@ function isDataTransferItemWithEmptyType(file) {
71
57
  *
72
58
  * Chrome/other browsers may return an empty MIME type for files during drag events,
73
59
  * so we accept those as well (we'll validate properly on drop).
74
- *
75
- * @param {File} file
76
- * @param {string} accept
77
- * @returns
78
60
  */
79
61
  function fileAccepted(file, accept) {
80
- const isAcceptable = file.type === "application/x-moz-file" || accepts(file, accept) || isDataTransferItemWithEmptyType(file);
62
+ const isAcceptable = file.type === "application/x-moz-file" || accepts(file, accept ?? "") || isDataTransferItemWithEmptyType(file);
81
63
  return [isAcceptable, isAcceptable ? null : getInvalidTypeRejectionErr(accept)];
82
64
  }
83
65
  function fileMatchSize(file, minSize, maxSize) {
@@ -93,19 +75,7 @@ function fileMatchSize(file, minSize, maxSize) {
93
75
  function isDefined(value) {
94
76
  return value !== void 0 && value !== null;
95
77
  }
96
- /**
97
- *
98
- * @param {object} options
99
- * @param {File[]} options.files
100
- * @param {string} [options.accept]
101
- * @param {number} [options.minSize]
102
- * @param {number} [options.maxSize]
103
- * @param {boolean} [options.multiple]
104
- * @param {number} [options.maxFiles]
105
- * @param {(f: File) => FileError|FileError[]|null} [options.validator]
106
- * @returns
107
- */
108
- function allFilesAccepted({ files, accept, minSize, maxSize, multiple, maxFiles, validator }) {
78
+ function allFilesAccepted({ files, accept, minSize, maxSize, multiple, maxFiles = 0, validator }) {
109
79
  if (!multiple && files.length > 1 || multiple && maxFiles >= 1 && files.length > maxFiles) return false;
110
80
  return files.every((file) => {
111
81
  const [accepted] = fileAccepted(file, accept);
@@ -136,14 +106,11 @@ function isIeOrEdge(userAgent = window.navigator.userAgent) {
136
106
  return isIe(userAgent) || isEdge(userAgent);
137
107
  }
138
108
  /**
139
- * This is intended to be used to compose event handlers
109
+ * This is intended to be used to compose event handlers.
140
110
  * They are executed in order until one of them calls `event.isPropagationStopped()`.
141
111
  * Note that the check is done on the first invoke too,
142
112
  * meaning that if propagation was stopped before invoking the fns,
143
113
  * no handlers will be executed.
144
- *
145
- * @param {Function} fns the event hanlder functions
146
- * @return {Function} the event handler to add to an element
147
114
  */
148
115
  function composeEventHandlers(...fns) {
149
116
  return (event, ...args) => fns.some((fn) => {
@@ -152,19 +119,13 @@ function composeEventHandlers(...fns) {
152
119
  });
153
120
  }
154
121
  /**
155
- * canUseFileSystemAccessAPI checks if the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API)
156
- * is supported by the browser.
157
- * @returns {boolean}
122
+ * canUseFileSystemAccessAPI checks if the File System Access API is supported by the browser.
158
123
  */
159
124
  function canUseFileSystemAccessAPI() {
160
125
  return "showOpenFilePicker" in window;
161
126
  }
162
127
  /**
163
- * Convert the `{accept}` dropzone prop to the
164
- * `{types}` option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
165
- *
166
- * @param {AcceptProp} accept
167
- * @returns {{accept: string[]}[]}
128
+ * Convert the `{accept}` dropzone prop to the `{types}` option for showOpenFilePicker.
168
129
  */
169
130
  function pickerOptionsFromAccept(accept) {
170
131
  if (isDefined(accept)) return [{
@@ -180,75 +141,47 @@ function pickerOptionsFromAccept(accept) {
180
141
  ok = false;
181
142
  }
182
143
  return ok;
183
- }).reduce((agg, [mimeType, ext]) => ({
184
- ...agg,
185
- [mimeType]: ext
186
- }), {})
144
+ }).reduce((agg, [mimeType, ext]) => {
145
+ agg[mimeType] = ext;
146
+ return agg;
147
+ }, {})
187
148
  }];
188
- return accept;
189
149
  }
190
150
  /**
191
151
  * Convert the `{accept}` dropzone prop to an array of MIME types/extensions.
192
- * @param {AcceptProp} accept
193
- * @returns {string}
194
152
  */
195
153
  function acceptPropAsAcceptAttr(accept) {
196
- if (isDefined(accept)) return Object.entries(accept).reduce((a, [mimeType, ext]) => [
197
- ...a,
198
- mimeType,
199
- ...ext
200
- ], []).filter((v) => isMIMEType(v) || isExt(v)).join(",");
154
+ if (isDefined(accept)) return Object.entries(accept).reduce((a, [mimeType, ext]) => {
155
+ a.push(mimeType, ...ext);
156
+ return a;
157
+ }, []).filter((v) => isMIMEType(v) || isExt(v)).join(",");
201
158
  }
202
159
  /**
203
160
  * Check if v is an exception caused by aborting a request (e.g window.showOpenFilePicker()).
204
- *
205
- * See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
206
- * @param {any} v
207
- * @returns {boolean} True if v is an abort exception.
208
161
  */
209
162
  function isAbort(v) {
210
163
  return v instanceof DOMException && (v.name === "AbortError" || v.code === v.ABORT_ERR);
211
164
  }
212
165
  /**
213
166
  * Check if v is a security error.
214
- *
215
- * See https://developer.mozilla.org/en-US/docs/Web/API/DOMException.
216
- * @param {any} v
217
- * @returns {boolean} True if v is a security error.
218
167
  */
219
168
  function isSecurityError(v) {
220
169
  return v instanceof DOMException && (v.name === "SecurityError" || v.code === v.SECURITY_ERR);
221
170
  }
222
171
  /**
223
172
  * Check if v is a MIME type string.
224
- *
225
- * See accepted format: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers.
226
- *
227
- * @param {string} v
228
173
  */
229
174
  function isMIMEType(v) {
230
175
  return v === "audio/*" || v === "video/*" || v === "image/*" || v === "text/*" || v === "application/*" || /\w+\/[-+.\w]+/g.test(v);
231
176
  }
232
177
  /**
233
178
  * Check if v is a file extension.
234
- * @param {string} v
235
179
  */
236
180
  function isExt(v) {
237
181
  return /^.*\.[\w]+$/.test(v);
238
182
  }
239
- /**
240
- * @typedef {Object.<string, string[]>} AcceptProp
241
- */
242
- /**
243
- * @typedef {object} FileError
244
- * @property {string} message
245
- * @property {ErrorCode|string} code
246
- */
247
- /**
248
- * @typedef {"file-invalid-type"|"file-too-large"|"file-too-small"|"too-many-files"} ErrorCode
249
- */
250
183
  //#endregion
251
- //#region src/index.jsx
184
+ //#region src/index.tsx
252
185
  /**
253
186
  * Convenience wrapper component for the `useDropzone` hook
254
187
  *
@@ -266,261 +199,12 @@ function isExt(v) {
266
199
  const Dropzone = forwardRef(({ children, ...params }, ref) => {
267
200
  const { open, ...props } = useDropzone(params);
268
201
  useImperativeHandle(ref, () => ({ open }), [open]);
269
- return /* @__PURE__ */ jsx(Fragment, { children: children({
202
+ return /* @__PURE__ */ jsx(Fragment, { children: children?.({
270
203
  ...props,
271
204
  open
272
205
  }) });
273
206
  });
274
207
  Dropzone.displayName = "Dropzone";
275
- const defaultProps = {
276
- disabled: false,
277
- getFilesFromEvent: fromEvent,
278
- maxSize: Infinity,
279
- minSize: 0,
280
- multiple: true,
281
- maxFiles: 0,
282
- preventDropOnDocument: true,
283
- noClick: false,
284
- noKeyboard: false,
285
- noDrag: false,
286
- noDragEventsBubbling: false,
287
- validator: null,
288
- useFsAccessApi: false,
289
- autoFocus: false
290
- };
291
- Dropzone.defaultProps = defaultProps;
292
- Dropzone.propTypes = {
293
- /**
294
- * Render function that exposes the dropzone state and prop getter fns
295
- *
296
- * @param {object} params
297
- * @param {Function} params.getRootProps Returns the props you should apply to the root drop container you render
298
- * @param {Function} params.getInputProps Returns the props you should apply to hidden file input you render
299
- * @param {Function} params.open Open the native file selection dialog
300
- * @param {boolean} params.isFocused Dropzone area is in focus
301
- * @param {boolean} params.isFileDialogActive File dialog is opened
302
- * @param {boolean} params.isDragActive Active drag is in progress
303
- * @param {boolean} params.isDragAccept Dragged files are accepted
304
- * @param {boolean} params.isDragReject True only during an active drag when some dragged files would be rejected. After drop, this resets to false. Use fileRejections for post-drop errors.
305
- * @param {boolean} params.isDragGlobal Files are being dragged anywhere on the document
306
- * @param {File[]} params.acceptedFiles Accepted files
307
- * @param {FileRejection[]} params.fileRejections Rejected files and why they were rejected. This persists after drop and is the source of truth for post-drop rejections.
308
- */
309
- children: PropTypes.func,
310
- /**
311
- * Set accepted file types.
312
- * Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
313
- * Keep in mind that mime type determination is not reliable across platforms. CSV files,
314
- * for example, are reported as text/plain under macOS but as application/vnd.ms-excel under
315
- * Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
316
- */
317
- accept: PropTypes.objectOf(PropTypes.arrayOf(PropTypes.string)),
318
- /**
319
- * Allow drag 'n' drop (or selection from the file dialog) of multiple files
320
- */
321
- multiple: PropTypes.bool,
322
- /**
323
- * If false, allow dropped items to take over the current browser window
324
- */
325
- preventDropOnDocument: PropTypes.bool,
326
- /**
327
- * If true, disables click to open the native file selection dialog
328
- */
329
- noClick: PropTypes.bool,
330
- /**
331
- * If true, disables SPACE/ENTER to open the native file selection dialog.
332
- * Note that it also stops tracking the focus state.
333
- */
334
- noKeyboard: PropTypes.bool,
335
- /**
336
- * If true, disables drag 'n' drop
337
- */
338
- noDrag: PropTypes.bool,
339
- /**
340
- * If true, stops drag event propagation to parents
341
- */
342
- noDragEventsBubbling: PropTypes.bool,
343
- /**
344
- * Minimum file size (in bytes)
345
- */
346
- minSize: PropTypes.number,
347
- /**
348
- * Maximum file size (in bytes)
349
- */
350
- maxSize: PropTypes.number,
351
- /**
352
- * Maximum accepted number of files
353
- * The default value is 0 which means there is no limitation to how many files are accepted.
354
- */
355
- maxFiles: PropTypes.number,
356
- /**
357
- * Enable/disable the dropzone
358
- */
359
- disabled: PropTypes.bool,
360
- /**
361
- * Use this to provide a custom file aggregator
362
- *
363
- * @param {(DragEvent|Event|Array<FileSystemFileHandle>)} event A drag event or input change event (if files were selected via the file dialog)
364
- */
365
- getFilesFromEvent: PropTypes.func,
366
- /**
367
- * Cb for when closing the file dialog with no selection
368
- */
369
- onFileDialogCancel: PropTypes.func,
370
- /**
371
- * Cb for when opening the file dialog
372
- */
373
- onFileDialogOpen: PropTypes.func,
374
- /**
375
- * Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
376
- * to open the file picker instead of using an `<input type="file">` click event.
377
- */
378
- useFsAccessApi: PropTypes.bool,
379
- /**
380
- * Set to true to focus the root element on render
381
- */
382
- autoFocus: PropTypes.bool,
383
- /**
384
- * Cb for when the `dragenter` event occurs.
385
- *
386
- * @param {DragEvent} event
387
- */
388
- onDragEnter: PropTypes.func,
389
- /**
390
- * Cb for when the `dragleave` event occurs
391
- *
392
- * @param {DragEvent} event
393
- */
394
- onDragLeave: PropTypes.func,
395
- /**
396
- * Cb for when the `dragover` event occurs
397
- *
398
- * @param {DragEvent} event
399
- */
400
- onDragOver: PropTypes.func,
401
- /**
402
- * Cb for when the `drop` event occurs.
403
- * Note that this callback is invoked after the `getFilesFromEvent` callback is done.
404
- *
405
- * Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and `maxSize` props.
406
- * `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) or a valid file extension.
407
- * If `multiple` is set to false and additional files are dropped,
408
- * all files besides the first will be rejected.
409
- * Any file which does not have a size in the [`minSize`, `maxSize`] range, will be rejected as well.
410
- *
411
- * Note that the `onDrop` callback will always be invoked regardless if the dropped files were accepted or rejected.
412
- * If you'd like to react to a specific scenario, use the `onDropAccepted`/`onDropRejected` props.
413
- *
414
- * `onDrop` will provide you with an array of [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects which you can then process and send to a server.
415
- * For example, with [SuperAgent](https://github.com/visionmedia/superagent) as a http/ajax library:
416
- *
417
- * ```js
418
- * function onDrop(acceptedFiles) {
419
- * const req = request.post('/upload')
420
- * acceptedFiles.forEach(file => {
421
- * req.attach(file.name, file)
422
- * })
423
- * req.end(callback)
424
- * }
425
- * ```
426
- *
427
- * @param {File[]} acceptedFiles
428
- * @param {FileRejection[]} fileRejections
429
- * @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
430
- */
431
- onDrop: PropTypes.func,
432
- /**
433
- * Cb for when the `drop` event occurs.
434
- * Note that if no files are accepted, this callback is not invoked.
435
- *
436
- * @param {File[]} files
437
- * @param {(DragEvent|Event)} event
438
- */
439
- onDropAccepted: PropTypes.func,
440
- /**
441
- * Cb for when the `drop` event occurs.
442
- * Note that if no files are rejected, this callback is not invoked.
443
- *
444
- * @param {FileRejection[]} fileRejections
445
- * @param {(DragEvent|Event)} event
446
- */
447
- onDropRejected: PropTypes.func,
448
- /**
449
- * Cb for when there's some error from any of the promises.
450
- *
451
- * @param {Error} error
452
- */
453
- onError: PropTypes.func,
454
- /**
455
- * Custom validation function. It must return null if there's no errors.
456
- * @param {File} file
457
- * @returns {FileError|FileError[]|null}
458
- */
459
- validator: PropTypes.func
460
- };
461
- /**
462
- * A function that is invoked for the `dragenter`,
463
- * `dragover` and `dragleave` events.
464
- * It is not invoked if the items are not files (such as link, text, etc.).
465
- *
466
- * @callback dragCb
467
- * @param {DragEvent} event
468
- */
469
- /**
470
- * A function that is invoked for the `drop` or input change event.
471
- * It is not invoked if the items are not files (such as link, text, etc.).
472
- *
473
- * @callback dropCb
474
- * @param {File[]} acceptedFiles List of accepted files
475
- * @param {FileRejection[]} fileRejections List of rejected files and why they were rejected. This is the authoritative source for post-drop file rejections.
476
- * @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
477
- */
478
- /**
479
- * A function that is invoked for the `drop` or input change event.
480
- * It is not invoked if the items are files (such as link, text, etc.).
481
- *
482
- * @callback dropAcceptedCb
483
- * @param {File[]} files List of accepted files that meet the given criteria
484
- * (`accept`, `multiple`, `minSize`, `maxSize`)
485
- * @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
486
- */
487
- /**
488
- * A function that is invoked for the `drop` or input change event.
489
- *
490
- * @callback dropRejectedCb
491
- * @param {File[]} files List of rejected files that do not meet the given criteria
492
- * (`accept`, `multiple`, `minSize`, `maxSize`)
493
- * @param {(DragEvent|Event)} event A drag event or input change event (if files were selected via the file dialog)
494
- */
495
- /**
496
- * A function that is used aggregate files,
497
- * in a asynchronous fashion, from drag or input change events.
498
- *
499
- * @callback getFilesFromEvent
500
- * @param {(DragEvent|Event|Array<FileSystemFileHandle>)} event A drag event or input change event (if files were selected via the file dialog)
501
- * @returns {(File[]|Promise<File[]>)}
502
- */
503
- /**
504
- * An object with the current dropzone state.
505
- *
506
- * @typedef {object} DropzoneState
507
- * @property {boolean} isFocused Dropzone area is in focus
508
- * @property {boolean} isFileDialogActive File dialog is opened
509
- * @property {boolean} isDragActive Active drag is in progress
510
- * @property {boolean} isDragAccept Dragged files are accepted
511
- * @property {boolean} isDragReject True only during an active drag when some dragged files would be rejected. After drop, this resets to false. Use fileRejections for post-drop errors.
512
- * @property {boolean} isDragGlobal Files are being dragged anywhere on the document
513
- * @property {File[]} acceptedFiles Accepted files
514
- * @property {FileRejection[]} fileRejections Rejected files and why they were rejected. This persists after drop and is the source of truth for post-drop rejections.
515
- */
516
- /**
517
- * An object with the dropzone methods.
518
- *
519
- * @typedef {object} DropzoneMethods
520
- * @property {Function} getRootProps Returns the props you should apply to the root drop container you render
521
- * @property {Function} getInputProps Returns the props you should apply to hidden file input you render
522
- * @property {Function} open Open the native file selection dialog
523
- */
524
208
  const initialState = {
525
209
  isFocused: false,
526
210
  isFileDialogActive: false,
@@ -549,81 +233,13 @@ const initialState = {
549
233
  * )
550
234
  * }
551
235
  * ```
552
- *
553
- * @function useDropzone
554
- *
555
- * @param {object} props
556
- * @param {import("./utils").AcceptProp} [props.accept] Set accepted file types.
557
- * Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
558
- * Keep in mind that mime type determination is not reliable across platforms. CSV files,
559
- * for example, are reported as text/plain under macOS but as application/vnd.ms-excel under
560
- * Windows. In some cases there might not be a mime type set at all (https://github.com/react-dropzone/react-dropzone/issues/276).
561
- * @param {boolean} [props.multiple=true] Allow drag 'n' drop (or selection from the file dialog) of multiple files
562
- * @param {boolean} [props.preventDropOnDocument=true] If false, allow dropped items to take over the current browser window
563
- * @param {boolean} [props.noClick=false] If true, disables click to open the native file selection dialog
564
- * @param {boolean} [props.noKeyboard=false] If true, disables SPACE/ENTER to open the native file selection dialog.
565
- * Note that it also stops tracking the focus state.
566
- * @param {boolean} [props.noDrag=false] If true, disables drag 'n' drop
567
- * @param {boolean} [props.noDragEventsBubbling=false] If true, stops drag event propagation to parents
568
- * @param {number} [props.minSize=0] Minimum file size (in bytes)
569
- * @param {number} [props.maxSize=Infinity] Maximum file size (in bytes)
570
- * @param {boolean} [props.disabled=false] Enable/disable the dropzone
571
- * @param {getFilesFromEvent} [props.getFilesFromEvent] Use this to provide a custom file aggregator
572
- * @param {Function} [props.onFileDialogCancel] Cb for when closing the file dialog with no selection
573
- * @param {boolean} [props.useFsAccessApi] Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
574
- * to open the file picker instead of using an `<input type="file">` click event.
575
- * @param {boolean} autoFocus Set to true to auto focus the root element.
576
- * @param {Function} [props.onFileDialogOpen] Cb for when opening the file dialog
577
- * @param {dragCb} [props.onDragEnter] Cb for when the `dragenter` event occurs.
578
- * @param {dragCb} [props.onDragLeave] Cb for when the `dragleave` event occurs
579
- * @param {dragCb} [props.onDragOver] Cb for when the `dragover` event occurs
580
- * @param {dropCb} [props.onDrop] Cb for when the `drop` event occurs.
581
- * Note that this callback is invoked after the `getFilesFromEvent` callback is done.
582
- *
583
- * Files are accepted or rejected based on the `accept`, `multiple`, `minSize` and `maxSize` props.
584
- * `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).
585
- * If `multiple` is set to false and additional files are dropped,
586
- * all files besides the first will be rejected.
587
- * Any file which does not have a size in the [`minSize`, `maxSize`] range, will be rejected as well.
588
- *
589
- * Note that the `onDrop` callback will always be invoked regardless if the dropped files were accepted or rejected.
590
- * If you'd like to react to a specific scenario, use the `onDropAccepted`/`onDropRejected` props.
591
- *
592
- * The second parameter (fileRejections) is the authoritative list of rejected files after a drop.
593
- * Use this parameter or the fileRejections state property to handle post-drop file rejections,
594
- * as isDragReject only indicates rejection state during active drag operations.
595
- *
596
- * `onDrop` will provide you with an array of [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects which you can then process and send to a server.
597
- * For example, with [SuperAgent](https://github.com/visionmedia/superagent) as a http/ajax library:
598
- *
599
- * ```js
600
- * function onDrop(acceptedFiles) {
601
- * const req = request.post('/upload')
602
- * acceptedFiles.forEach(file => {
603
- * req.attach(file.name, file)
604
- * })
605
- * req.end(callback)
606
- * }
607
- * ```
608
- * @param {dropAcceptedCb} [props.onDropAccepted]
609
- * @param {dropRejectedCb} [props.onDropRejected]
610
- * @param {(error: Error) => void} [props.onError]
611
- *
612
- * @returns {DropzoneState & DropzoneMethods}
613
236
  */
614
237
  function useDropzone(props = {}) {
615
- const { accept, disabled, getFilesFromEvent, maxSize, minSize, multiple, maxFiles, onDragEnter, onDragLeave, onDragOver, onDrop, onDropAccepted, onDropRejected, onFileDialogCancel, onFileDialogOpen, useFsAccessApi, autoFocus, preventDropOnDocument, noClick, noKeyboard, noDrag, noDragEventsBubbling, onError, validator } = {
616
- ...defaultProps,
617
- ...props
618
- };
238
+ 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 } = props;
619
239
  const acceptAttr = useMemo(() => acceptPropAsAcceptAttr(accept), [accept]);
620
240
  const pickerTypes = useMemo(() => pickerOptionsFromAccept(accept), [accept]);
621
241
  const onFileDialogOpenCb = useMemo(() => typeof onFileDialogOpen === "function" ? onFileDialogOpen : noop, [onFileDialogOpen]);
622
242
  const onFileDialogCancelCb = useMemo(() => typeof onFileDialogCancel === "function" ? onFileDialogCancel : noop, [onFileDialogCancel]);
623
- /**
624
- * @constant
625
- * @type {React.MutableRefObject<HTMLElement>}
626
- */
627
243
  const rootRef = useRef(null);
628
244
  const inputRef = useRef(null);
629
245
  const [state, dispatch] = useReducer(reducer, initialState);
@@ -633,7 +249,7 @@ function useDropzone(props = {}) {
633
249
  if (!fsAccessApiWorksRef.current && isFileDialogActive) setTimeout(() => {
634
250
  if (inputRef.current) {
635
251
  const { files } = inputRef.current;
636
- if (!files.length) {
252
+ if (!files?.length) {
637
253
  dispatch({ type: "closeDialog" });
638
254
  onFileDialogCancelCb();
639
255
  }
@@ -654,7 +270,7 @@ function useDropzone(props = {}) {
654
270
  const dragTargetsRef = useRef([]);
655
271
  const globalDragTargetsRef = useRef([]);
656
272
  const onDocumentDrop = (event) => {
657
- if (rootRef.current && rootRef.current.contains(event.target)) return;
273
+ if (rootRef.current && event.target && rootRef.current.contains(event.target)) return;
658
274
  event.preventDefault();
659
275
  dragTargetsRef.current = [];
660
276
  };
@@ -672,7 +288,7 @@ function useDropzone(props = {}) {
672
288
  }, [rootRef, preventDropOnDocument]);
673
289
  useEffect(() => {
674
290
  const onDocumentDragEnter = (event) => {
675
- globalDragTargetsRef.current = [...globalDragTargetsRef.current, event.target];
291
+ if (event.target) globalDragTargetsRef.current = [...globalDragTargetsRef.current, event.target];
676
292
  if (isEvtWithFiles(event)) dispatch({
677
293
  isDragGlobal: true,
678
294
  type: "setDragGlobal"
@@ -725,7 +341,7 @@ function useDropzone(props = {}) {
725
341
  }, [onError]);
726
342
  const onDragEnterCb = useCallback((event) => {
727
343
  event.preventDefault();
728
- event.persist();
344
+ event.persist?.();
729
345
  stopPropagation(event);
730
346
  dragTargetsRef.current = [...dragTargetsRef.current, event.target];
731
347
  if (isEvtWithFiles(event)) Promise.resolve(getFilesFromEvent(event)).then((files) => {
@@ -762,7 +378,7 @@ function useDropzone(props = {}) {
762
378
  ]);
763
379
  const onDragOverCb = useCallback((event) => {
764
380
  event.preventDefault();
765
- event.persist();
381
+ event.persist?.();
766
382
  stopPropagation(event);
767
383
  const hasFiles = isEvtWithFiles(event);
768
384
  if (hasFiles && event.dataTransfer) try {
@@ -773,9 +389,9 @@ function useDropzone(props = {}) {
773
389
  }, [onDragOver, noDragEventsBubbling]);
774
390
  const onDragLeaveCb = useCallback((event) => {
775
391
  event.preventDefault();
776
- event.persist();
392
+ event.persist?.();
777
393
  stopPropagation(event);
778
- const targets = dragTargetsRef.current.filter((target) => rootRef.current && rootRef.current.contains(target));
394
+ const targets = dragTargetsRef.current.filter((target) => rootRef.current?.contains(target));
779
395
  const targetIdx = targets.indexOf(event.target);
780
396
  if (targetIdx !== -1) targets.splice(targetIdx, 1);
781
397
  dragTargetsRef.current = targets;
@@ -805,7 +421,7 @@ function useDropzone(props = {}) {
805
421
  if (customErrors) errors = errors.concat(customErrors);
806
422
  fileRejections.push({
807
423
  file,
808
- errors: errors.filter((e) => e)
424
+ errors: errors.filter((e) => e != null)
809
425
  });
810
426
  }
811
427
  });
@@ -840,7 +456,7 @@ function useDropzone(props = {}) {
840
456
  ]);
841
457
  const onDropCb = useCallback((event) => {
842
458
  event.preventDefault();
843
- event.persist();
459
+ event.persist?.();
844
460
  stopPropagation(event);
845
461
  dragTargetsRef.current = [];
846
462
  if (isEvtWithFiles(event)) Promise.resolve(getFilesFromEvent(event)).then((files) => {
@@ -872,7 +488,7 @@ function useDropzone(props = {}) {
872
488
  } else if (isSecurityError(e)) {
873
489
  fsAccessApiWorksRef.current = false;
874
490
  if (inputRef.current) {
875
- inputRef.current.value = null;
491
+ inputRef.current.value = "";
876
492
  inputRef.current.click();
877
493
  } else onErrCb(/* @__PURE__ */ new Error("Cannot open the file picker because the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API is not supported and no <input> was provided."));
878
494
  } else onErrCb(e);
@@ -882,7 +498,7 @@ function useDropzone(props = {}) {
882
498
  if (inputRef.current) {
883
499
  dispatch({ type: "openDialog" });
884
500
  onFileDialogOpenCb();
885
- inputRef.current.value = null;
501
+ inputRef.current.value = "";
886
502
  inputRef.current.click();
887
503
  }
888
504
  }, [
@@ -896,7 +512,7 @@ function useDropzone(props = {}) {
896
512
  multiple
897
513
  ]);
898
514
  const onKeyDownCb = useCallback((event) => {
899
- if (!rootRef.current || !rootRef.current.isEqualNode(event.target)) return;
515
+ if (!rootRef.current?.isEqualNode(event.target)) return;
900
516
  if (event.key === " " || event.key === "Enter" || event.keyCode === 32 || event.keyCode === 13) {
901
517
  event.preventDefault();
902
518
  openFileDialog();
@@ -995,13 +611,7 @@ function useDropzone(props = {}) {
995
611
  open: composeHandler(openFileDialog)
996
612
  };
997
613
  }
998
- /**
999
- * @param {DropzoneState} state
1000
- * @param {{type: string} & DropzoneState} action
1001
- * @returns {DropzoneState}
1002
- */
1003
614
  function reducer(state, action) {
1004
- /* istanbul ignore next */
1005
615
  switch (action.type) {
1006
616
  case "focus": return {
1007
617
  ...state,