@georapbox/files-dropzone-element 2.0.0 → 2.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/LICENSE +1 -1
- package/README.md +60 -4
- package/dist/files-dropzone-defined.js +21 -8
- package/dist/files-dropzone-defined.js.map +7 -1
- package/dist/files-dropzone.d.ts +42 -1
- package/dist/files-dropzone.d.ts.map +1 -1
- package/dist/files-dropzone.js +21 -8
- package/dist/files-dropzone.js.map +7 -1
- package/dist/utils/files-selector.d.ts.map +1 -1
- package/dist/utils/is-valid-file.d.ts +1 -1
- package/dist/utils/is-valid-file.d.ts.map +1 -1
- package/package.json +22 -39
- package/src/files-dropzone.js +122 -72
- package/src/utils/files-selector.js +1 -1
- package/src/utils/is-valid-file.js +6 -1
package/package.json
CHANGED
|
@@ -1,51 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@georapbox/files-dropzone-element",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A custom element that creates a drag and drop zone for files",
|
|
5
5
|
"main": "dist/files-dropzone.js",
|
|
6
6
|
"module": "dist/files-dropzone.js",
|
|
7
7
|
"unpkg": "dist/files-dropzone.js",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"types": "dist/files-dropzone.d.ts",
|
|
10
|
-
"targets": {
|
|
11
|
-
"files-dropzone": {
|
|
12
|
-
"context": "browser",
|
|
13
|
-
"outputFormat": "esmodule",
|
|
14
|
-
"source": "src/files-dropzone.js",
|
|
15
|
-
"optimize": true,
|
|
16
|
-
"isLibrary": true,
|
|
17
|
-
"distDir": "dist",
|
|
18
|
-
"engines": {
|
|
19
|
-
"browsers": "> 0.5%, last 2 versions, not dead"
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"files-dropzone-defined": {
|
|
23
|
-
"context": "browser",
|
|
24
|
-
"outputFormat": "esmodule",
|
|
25
|
-
"source": "src/files-dropzone-defined.js",
|
|
26
|
-
"optimize": true,
|
|
27
|
-
"isLibrary": true,
|
|
28
|
-
"distDir": "dist",
|
|
29
|
-
"engines": {
|
|
30
|
-
"browsers": "> 0.5%, last 2 versions, not dead"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
10
|
"files": [
|
|
35
11
|
"/src",
|
|
36
12
|
"/dist"
|
|
37
13
|
],
|
|
38
14
|
"scripts": {
|
|
39
|
-
"lint": "eslint
|
|
15
|
+
"lint": "eslint",
|
|
16
|
+
"format": "prettier --ignore-unknown --write .",
|
|
40
17
|
"test": "web-test-runner",
|
|
41
18
|
"test:watch": "npm run test -- --watch",
|
|
42
19
|
"test:coverage": "npm run test -- --coverage",
|
|
43
20
|
"types": "tsc --project tsconfig.json",
|
|
44
|
-
"dev:
|
|
45
|
-
"dev": "npm-run-all clean dev:
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
21
|
+
"dev:esbuild": "node ./scripts/dev.mjs",
|
|
22
|
+
"dev": "npm-run-all clean dev:esbuild",
|
|
23
|
+
"serve:dev": "web-dev-server --node-resolve --open /docs/ --watch",
|
|
24
|
+
"start": "npm-run-all --parallel \"serve:dev -- {1}\" dev",
|
|
25
|
+
"build:esbuild": "node ./scripts/build.mjs",
|
|
26
|
+
"build": "npm-run-all clean build:esbuild",
|
|
27
|
+
"clean": "rimraf dist",
|
|
49
28
|
"prepare": "npm-run-all clean lint test build types"
|
|
50
29
|
},
|
|
51
30
|
"keywords": [
|
|
@@ -67,17 +46,21 @@
|
|
|
67
46
|
},
|
|
68
47
|
"homepage": "https://github.com/georapbox/files-dropzone-element#readme",
|
|
69
48
|
"devDependencies": {
|
|
70
|
-
"@
|
|
49
|
+
"@eslint/js": "~9.34.0",
|
|
50
|
+
"@esm-bundle/chai": "~4.3.4",
|
|
71
51
|
"@open-wc/testing": "~4.0.0",
|
|
72
|
-
"@web/
|
|
52
|
+
"@web/dev-server": "~0.4.6",
|
|
53
|
+
"@web/test-runner": "~0.20.2",
|
|
73
54
|
"@web/test-runner-playwright": "~0.11.0",
|
|
74
|
-
"
|
|
75
|
-
"eslint
|
|
55
|
+
"esbuild": "~0.25.9",
|
|
56
|
+
"eslint": "~9.34.0",
|
|
57
|
+
"eslint-plugin-wc": "~3.0.1",
|
|
58
|
+
"globals": "~16.3.0",
|
|
76
59
|
"npm-run-all": "~4.1.5",
|
|
77
|
-
"
|
|
78
|
-
"rimraf": "~
|
|
79
|
-
"sinon": "~
|
|
80
|
-
"typescript": "~5.
|
|
60
|
+
"prettier": "~3.6.2",
|
|
61
|
+
"rimraf": "~6.0.1",
|
|
62
|
+
"sinon": "~21.0.0",
|
|
63
|
+
"typescript": "~5.9.2"
|
|
81
64
|
},
|
|
82
65
|
"publishConfig": {
|
|
83
66
|
"access": "public"
|
package/src/files-dropzone.js
CHANGED
|
@@ -7,17 +7,35 @@
|
|
|
7
7
|
* @typedef {T | null} Nullable
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Stable identifier for error codes used in the `files-dropzone-error` event.
|
|
12
|
+
*
|
|
13
|
+
* @typedef {typeof FilesDropzone.ERROR_CODES[keyof typeof FilesDropzone.ERROR_CODES]} FilesDropzoneErrorCode
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The detail payload for the `files-dropzone-error` event.
|
|
18
|
+
*
|
|
19
|
+
* @typedef {Object} FilesDropzoneErrorDetail
|
|
20
|
+
* @property {FilesDropzoneErrorCode} code - Stable identifier for the type of error that occurred.
|
|
21
|
+
* @property {unknown} error - The underlying error object or value that triggered the event.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Stable identifier for rejection codes used in the various drop-related events.
|
|
26
|
+
*
|
|
27
|
+
* @typedef {typeof FilesDropzone.REJECTION_CODES[keyof typeof FilesDropzone.REJECTION_CODES]} FilesDropzoneRejectionCode
|
|
28
|
+
*/
|
|
29
|
+
|
|
10
30
|
import { isValidFile } from './utils/is-valid-file.js';
|
|
11
31
|
import { getFilesFromEvent } from './utils/files-selector.js';
|
|
12
32
|
|
|
33
|
+
const css = String.raw;
|
|
34
|
+
const html = String.raw;
|
|
13
35
|
const COMPONENT_NAME = 'files-dropzone';
|
|
14
|
-
const TOO_MANY_FILES = 'TOO_MANY_FILES';
|
|
15
|
-
const FILE_TOO_LARGE = 'FILE_TOO_LARGE';
|
|
16
|
-
const FILE_TOO_SMALL = 'FILE_TOO_SMALL';
|
|
17
|
-
const INVALID_MIME_TYPE = 'INVALID_MIME_TYPE';
|
|
18
36
|
const template = document.createElement('template');
|
|
19
37
|
|
|
20
|
-
const styles =
|
|
38
|
+
const styles = css`
|
|
21
39
|
*,
|
|
22
40
|
*::before,
|
|
23
41
|
*::after {
|
|
@@ -42,7 +60,7 @@ const styles = /* css */`
|
|
|
42
60
|
--dropzone-body-color: #3f3f46;
|
|
43
61
|
--dropzone-body-color-dragover: var(--dropzone-body-color);
|
|
44
62
|
--dropzone-body-color-hover: var(--dropzone-body-color-dragover);
|
|
45
|
-
--dropzone-focus-shadow-rgb: 49,132,253;
|
|
63
|
+
--dropzone-focus-shadow-rgb: 49, 132, 253;
|
|
46
64
|
--dropzone-focus-box-shadow: 0 0 0 0.25rem rgba(var(--dropzone-focus-shadow-rgb), 0.5);
|
|
47
65
|
--transition-duration: 0.2s; /* for backwards compatibility */
|
|
48
66
|
--dropzone-transition-duration: var(--transition-duration);
|
|
@@ -59,7 +77,11 @@ const styles = /* css */`
|
|
|
59
77
|
color: var(--dropzone-body-color);
|
|
60
78
|
text-align: center;
|
|
61
79
|
cursor: pointer;
|
|
62
|
-
transition:
|
|
80
|
+
transition:
|
|
81
|
+
border var(--dropzone-transition-duration) ease-in-out,
|
|
82
|
+
background-color var(--dropzone-transition-duration) ease-in-out,
|
|
83
|
+
color var(--dropzone-transition-duration) ease-in-out,
|
|
84
|
+
box-shadow var(--dropzone-transition-duration) ease-in-out;
|
|
63
85
|
}
|
|
64
86
|
|
|
65
87
|
:host(:not([no-style])[disabled]) .dropzone {
|
|
@@ -88,15 +110,15 @@ const styles = /* css */`
|
|
|
88
110
|
}
|
|
89
111
|
`;
|
|
90
112
|
|
|
91
|
-
template.innerHTML =
|
|
113
|
+
template.innerHTML = html`
|
|
92
114
|
<style>
|
|
93
115
|
${styles}
|
|
94
116
|
</style>
|
|
95
117
|
|
|
96
|
-
<input type="file" id="file-input" hidden
|
|
118
|
+
<input type="file" id="file-input" hidden />
|
|
97
119
|
|
|
98
120
|
<div part="dropzone" class="dropzone" id="dropzone" tabindex="0" role="button" aria-disabled="false">
|
|
99
|
-
<slot>Drag 'n' drop files here, or click to select files</slot>
|
|
121
|
+
<slot><span>Drag 'n' drop files here, or click to select files</span></slot>
|
|
100
122
|
</div>
|
|
101
123
|
`;
|
|
102
124
|
|
|
@@ -157,6 +179,28 @@ template.innerHTML = /* html */`
|
|
|
157
179
|
* @method openFileDialog - Instance method. Opens the file dialog programmatically.
|
|
158
180
|
*/
|
|
159
181
|
class FilesDropzone extends HTMLElement {
|
|
182
|
+
/**
|
|
183
|
+
* Central list of error codes used in drop error event.
|
|
184
|
+
* @readonly
|
|
185
|
+
*/
|
|
186
|
+
static ERROR_CODES = /** @type {const} */ ({
|
|
187
|
+
FILE_DIALOG_OPEN_FAILED: 'FILE_DIALOG_OPEN_FAILED',
|
|
188
|
+
FILE_INPUT_CHANGE_FAILED: 'FILE_INPUT_CHANGE_FAILED',
|
|
189
|
+
DROP_EVENT_PROCESSING_FAILED: 'DROP_EVENT_PROCESSING_FAILED',
|
|
190
|
+
UNKNOWN_ERROR: 'UNKNOWN_ERROR'
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Central list of rejection codes for files that are rejected during the drop process.
|
|
195
|
+
* @readonly
|
|
196
|
+
*/
|
|
197
|
+
static REJECTION_CODES = /** @type {const} */ ({
|
|
198
|
+
TOO_MANY_FILES: 'TOO_MANY_FILES',
|
|
199
|
+
FILE_TOO_LARGE: 'FILE_TOO_LARGE',
|
|
200
|
+
FILE_TOO_SMALL: 'FILE_TOO_SMALL',
|
|
201
|
+
INVALID_MIME_TYPE: 'INVALID_MIME_TYPE'
|
|
202
|
+
});
|
|
203
|
+
|
|
160
204
|
/** @type {Nullable<HTMLInputElement>} */
|
|
161
205
|
#fileInput = null;
|
|
162
206
|
|
|
@@ -172,7 +216,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
172
216
|
}
|
|
173
217
|
|
|
174
218
|
if (this.shadowRoot) {
|
|
175
|
-
this.#fileInput = /** @type {Nullable<HTMLInputElement>} */(this.shadowRoot.getElementById('file-input'));
|
|
219
|
+
this.#fileInput = /** @type {Nullable<HTMLInputElement>} */ (this.shadowRoot.getElementById('file-input'));
|
|
176
220
|
this.#dropzoneEl = this.shadowRoot.getElementById('dropzone');
|
|
177
221
|
}
|
|
178
222
|
}
|
|
@@ -372,6 +416,40 @@ class FilesDropzone extends HTMLElement {
|
|
|
372
416
|
this.toggleAttribute('no-style', !!value);
|
|
373
417
|
}
|
|
374
418
|
|
|
419
|
+
/**
|
|
420
|
+
* Emit a custom event with the given name and detail.
|
|
421
|
+
*
|
|
422
|
+
* @template D
|
|
423
|
+
* @param {string} name - The name of the event.
|
|
424
|
+
* @param {D} [detail] - The detail payload of the event.
|
|
425
|
+
* @param {CustomEventInit<D>} [init] - Override the default event initialization options.
|
|
426
|
+
* @returns {boolean} - Returns false if at least one event listener called `preventDefault()`, otherwise true.
|
|
427
|
+
*/
|
|
428
|
+
#emitEvent(name, detail, init) {
|
|
429
|
+
const options = {
|
|
430
|
+
bubbles: true,
|
|
431
|
+
composed: true,
|
|
432
|
+
cancelable: false,
|
|
433
|
+
...init,
|
|
434
|
+
detail
|
|
435
|
+
};
|
|
436
|
+
const evt = new CustomEvent(`${COMPONENT_NAME}-${name}`, options);
|
|
437
|
+
return this.dispatchEvent(evt);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Emit a standardized error event with typed detail.
|
|
442
|
+
*
|
|
443
|
+
* @param {FilesDropzoneErrorCode} code - Stable identifier for the type of error that occurred.
|
|
444
|
+
* @param {unknown} error - The underlying error that triggered the event.
|
|
445
|
+
*/
|
|
446
|
+
#emitErrorEvent(code, error) {
|
|
447
|
+
/** @type {FilesDropzoneErrorDetail} */
|
|
448
|
+
const detail = { code, error };
|
|
449
|
+
|
|
450
|
+
this.#emitEvent('error', detail);
|
|
451
|
+
}
|
|
452
|
+
|
|
375
453
|
/**
|
|
376
454
|
* Handles the change event of the file input.
|
|
377
455
|
*
|
|
@@ -380,12 +458,8 @@ class FilesDropzone extends HTMLElement {
|
|
|
380
458
|
#handleFileInputChange = async evt => {
|
|
381
459
|
try {
|
|
382
460
|
this.#handleFilesSelect(await getFilesFromEvent(evt));
|
|
383
|
-
} catch (
|
|
384
|
-
this.
|
|
385
|
-
bubbles: true,
|
|
386
|
-
composed: true,
|
|
387
|
-
detail: { error }
|
|
388
|
-
}));
|
|
461
|
+
} catch (err) {
|
|
462
|
+
this.#emitErrorEvent(FilesDropzone.ERROR_CODES.FILE_INPUT_CHANGE_FAILED, err);
|
|
389
463
|
}
|
|
390
464
|
};
|
|
391
465
|
|
|
@@ -397,10 +471,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
397
471
|
return;
|
|
398
472
|
}
|
|
399
473
|
|
|
400
|
-
this
|
|
401
|
-
bubbles: true,
|
|
402
|
-
composed: true
|
|
403
|
-
}));
|
|
474
|
+
this.#emitEvent('dragenter');
|
|
404
475
|
};
|
|
405
476
|
|
|
406
477
|
/**
|
|
@@ -423,10 +494,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
423
494
|
this.#dropzoneEl.part.add('dropzone--dragover');
|
|
424
495
|
}
|
|
425
496
|
|
|
426
|
-
this
|
|
427
|
-
bubbles: true,
|
|
428
|
-
composed: true
|
|
429
|
-
}));
|
|
497
|
+
this.#emitEvent('dragover');
|
|
430
498
|
};
|
|
431
499
|
|
|
432
500
|
/**
|
|
@@ -442,10 +510,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
442
510
|
this.#dropzoneEl.part.remove('dropzone--dragover');
|
|
443
511
|
}
|
|
444
512
|
|
|
445
|
-
this
|
|
446
|
-
bubbles: true,
|
|
447
|
-
composed: true
|
|
448
|
-
}));
|
|
513
|
+
this.#emitEvent('dragleave');
|
|
449
514
|
};
|
|
450
515
|
|
|
451
516
|
/**
|
|
@@ -467,12 +532,8 @@ class FilesDropzone extends HTMLElement {
|
|
|
467
532
|
|
|
468
533
|
try {
|
|
469
534
|
this.#handleFilesSelect(await getFilesFromEvent(evt));
|
|
470
|
-
} catch (
|
|
471
|
-
this.
|
|
472
|
-
bubbles: true,
|
|
473
|
-
composed: true,
|
|
474
|
-
detail: { error }
|
|
475
|
-
}));
|
|
535
|
+
} catch (err) {
|
|
536
|
+
this.#emitErrorEvent(FilesDropzone.ERROR_CODES.DROP_EVENT_PROCESSING_FAILED, err);
|
|
476
537
|
}
|
|
477
538
|
};
|
|
478
539
|
|
|
@@ -484,7 +545,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
484
545
|
return;
|
|
485
546
|
}
|
|
486
547
|
|
|
487
|
-
this
|
|
548
|
+
this.openFileDialog();
|
|
488
549
|
};
|
|
489
550
|
|
|
490
551
|
/**
|
|
@@ -498,7 +559,7 @@ class FilesDropzone extends HTMLElement {
|
|
|
498
559
|
}
|
|
499
560
|
|
|
500
561
|
if (evt.key === ' ' || evt.key === 'Enter') {
|
|
501
|
-
this
|
|
562
|
+
this.openFileDialog();
|
|
502
563
|
}
|
|
503
564
|
};
|
|
504
565
|
|
|
@@ -521,10 +582,12 @@ class FilesDropzone extends HTMLElement {
|
|
|
521
582
|
for (const file of files) {
|
|
522
583
|
rejectedFiles.push({
|
|
523
584
|
file,
|
|
524
|
-
errors: [
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
585
|
+
errors: [
|
|
586
|
+
{
|
|
587
|
+
code: FilesDropzone.REJECTION_CODES.TOO_MANY_FILES,
|
|
588
|
+
message: `Too many files selected. Only 1 file is allowed.`
|
|
589
|
+
}
|
|
590
|
+
]
|
|
528
591
|
});
|
|
529
592
|
}
|
|
530
593
|
} else if (this.multiple && filesLength > this.maxFiles) {
|
|
@@ -533,10 +596,12 @@ class FilesDropzone extends HTMLElement {
|
|
|
533
596
|
for (const file of files) {
|
|
534
597
|
rejectedFiles.push({
|
|
535
598
|
file,
|
|
536
|
-
errors: [
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
599
|
+
errors: [
|
|
600
|
+
{
|
|
601
|
+
code: FilesDropzone.REJECTION_CODES.TOO_MANY_FILES,
|
|
602
|
+
message: `Too many files selected. Only ${this.maxFiles} ${this.maxFiles > 1 ? 'files are' : 'file is'} allowed.`
|
|
603
|
+
}
|
|
604
|
+
]
|
|
540
605
|
});
|
|
541
606
|
}
|
|
542
607
|
} else {
|
|
@@ -554,21 +619,21 @@ class FilesDropzone extends HTMLElement {
|
|
|
554
619
|
|
|
555
620
|
if (!fileHasValidType) {
|
|
556
621
|
errors.push({
|
|
557
|
-
code: INVALID_MIME_TYPE,
|
|
622
|
+
code: FilesDropzone.REJECTION_CODES.INVALID_MIME_TYPE,
|
|
558
623
|
message: `File type "${file.type}" is not accepted.`
|
|
559
624
|
});
|
|
560
625
|
}
|
|
561
626
|
|
|
562
627
|
if (fileExceedsMaxSize) {
|
|
563
628
|
errors.push({
|
|
564
|
-
code: FILE_TOO_LARGE,
|
|
629
|
+
code: FilesDropzone.REJECTION_CODES.FILE_TOO_LARGE,
|
|
565
630
|
message: `File size ${file.size} exceeds the maximum size of ${this.maxSize}.`
|
|
566
631
|
});
|
|
567
632
|
}
|
|
568
633
|
|
|
569
634
|
if (fileIsSmallerThanMinSize) {
|
|
570
635
|
errors.push({
|
|
571
|
-
code: FILE_TOO_SMALL,
|
|
636
|
+
code: FilesDropzone.REJECTION_CODES.FILE_TOO_SMALL,
|
|
572
637
|
message: `File size ${file.size} is smaller than the minimum size of ${this.minSize}.`
|
|
573
638
|
});
|
|
574
639
|
}
|
|
@@ -578,33 +643,14 @@ class FilesDropzone extends HTMLElement {
|
|
|
578
643
|
}
|
|
579
644
|
}
|
|
580
645
|
|
|
581
|
-
this
|
|
582
|
-
bubbles: true,
|
|
583
|
-
composed: true,
|
|
584
|
-
detail: {
|
|
585
|
-
acceptedFiles,
|
|
586
|
-
rejectedFiles
|
|
587
|
-
}
|
|
588
|
-
}));
|
|
646
|
+
this.#emitEvent('drop', { acceptedFiles, rejectedFiles });
|
|
589
647
|
|
|
590
648
|
if (acceptedFiles.length > 0) {
|
|
591
|
-
this
|
|
592
|
-
bubbles: true,
|
|
593
|
-
composed: true,
|
|
594
|
-
detail: {
|
|
595
|
-
acceptedFiles
|
|
596
|
-
}
|
|
597
|
-
}));
|
|
649
|
+
this.#emitEvent('drop-accepted', { acceptedFiles });
|
|
598
650
|
}
|
|
599
651
|
|
|
600
652
|
if (rejectedFiles.length > 0) {
|
|
601
|
-
this
|
|
602
|
-
bubbles: true,
|
|
603
|
-
composed: true,
|
|
604
|
-
detail: {
|
|
605
|
-
rejectedFiles
|
|
606
|
-
}
|
|
607
|
-
}));
|
|
653
|
+
this.#emitEvent('drop-rejected', { rejectedFiles });
|
|
608
654
|
}
|
|
609
655
|
|
|
610
656
|
if (this.#fileInput) {
|
|
@@ -616,11 +662,15 @@ class FilesDropzone extends HTMLElement {
|
|
|
616
662
|
* Opens the file dialog programmatically.
|
|
617
663
|
*/
|
|
618
664
|
openFileDialog() {
|
|
619
|
-
if (this.disabled) {
|
|
665
|
+
if (this.disabled || !this.#fileInput) {
|
|
620
666
|
return;
|
|
621
667
|
}
|
|
622
668
|
|
|
623
|
-
|
|
669
|
+
try {
|
|
670
|
+
this.#fileInput.showPicker();
|
|
671
|
+
} catch (err) {
|
|
672
|
+
this.#emitErrorEvent(FilesDropzone.ERROR_CODES.FILE_DIALOG_OPEN_FAILED, err);
|
|
673
|
+
}
|
|
624
674
|
}
|
|
625
675
|
|
|
626
676
|
/**
|
|
@@ -239,7 +239,7 @@ const getFilesFromDataTransferItemList = async dataTransferItemList => {
|
|
|
239
239
|
files.push(file);
|
|
240
240
|
}
|
|
241
241
|
} else if (entry.isDirectory) {
|
|
242
|
-
queue.push(...await readAllDirectoryEntries(entry.createReader()));
|
|
242
|
+
queue.push(...(await readAllDirectoryEntries(entry.createReader())));
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
|
|
@@ -13,7 +13,12 @@ export function isValidFile(file, acceptedTypeSpecifiers = '') {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const acceptedMimeTypesList = [
|
|
16
|
-
...new Set(
|
|
16
|
+
...new Set(
|
|
17
|
+
acceptedTypeSpecifiers
|
|
18
|
+
.split(',')
|
|
19
|
+
.map(v => v.trim())
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
)
|
|
17
22
|
];
|
|
18
23
|
|
|
19
24
|
const fileMimeType = file.type;
|