react-dropzone 12.1.0 → 13.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/README.md +0 -2
- package/dist/es/index.js +71 -44
- package/dist/es/utils/index.js +88 -20
- package/dist/index.js +3 -3
- package/examples/accept/README.md +19 -16
- package/examples/pintura/README.md +3 -1
- package/examples/previews/README.md +3 -1
- package/examples/styling/README.md +2 -2
- package/package.json +1 -1
- package/src/index.js +69 -42
- package/src/index.spec.js +203 -25
- package/src/utils/index.js +82 -26
- package/src/utils/index.spec.js +124 -58
- package/typings/react-dropzone.d.ts +5 -1
- package/typings/tests/accept.tsx +3 -37
- package/typings/tests/all.tsx +3 -1
package/src/index.spec.js
CHANGED
|
@@ -41,7 +41,9 @@ describe("useDropzone() hook", () => {
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
it("sets {accept} prop on the <input>", () => {
|
|
44
|
-
const accept =
|
|
44
|
+
const accept = {
|
|
45
|
+
"image/jpeg": [],
|
|
46
|
+
};
|
|
45
47
|
const { container } = render(
|
|
46
48
|
<Dropzone accept={accept}>
|
|
47
49
|
{({ getRootProps, getInputProps }) => (
|
|
@@ -54,12 +56,16 @@ describe("useDropzone() hook", () => {
|
|
|
54
56
|
|
|
55
57
|
const input = container.querySelector("input");
|
|
56
58
|
|
|
57
|
-
expect(input).toHaveAttribute("accept",
|
|
59
|
+
expect(input).toHaveAttribute("accept", "image/jpeg");
|
|
58
60
|
});
|
|
59
61
|
|
|
60
62
|
it("updates {multiple} prop on the <input> when it changes", () => {
|
|
61
63
|
const { container, rerender } = render(
|
|
62
|
-
<Dropzone
|
|
64
|
+
<Dropzone
|
|
65
|
+
accept={{
|
|
66
|
+
"image/jpeg": [],
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
63
69
|
{({ getRootProps, getInputProps }) => (
|
|
64
70
|
<div {...getRootProps()}>
|
|
65
71
|
<input {...getInputProps()} />
|
|
@@ -74,7 +80,11 @@ describe("useDropzone() hook", () => {
|
|
|
74
80
|
);
|
|
75
81
|
|
|
76
82
|
rerender(
|
|
77
|
-
<Dropzone
|
|
83
|
+
<Dropzone
|
|
84
|
+
accept={{
|
|
85
|
+
"image/png": [],
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
78
88
|
{({ getRootProps, getInputProps }) => (
|
|
79
89
|
<div {...getRootProps()}>
|
|
80
90
|
<input {...getInputProps()} />
|
|
@@ -968,6 +978,42 @@ describe("useDropzone() hook", () => {
|
|
|
968
978
|
|
|
969
979
|
expect(props.getFilesFromEvent).toHaveBeenCalledTimes(2);
|
|
970
980
|
});
|
|
981
|
+
|
|
982
|
+
it("calls {onError} when getFilesFromEvent() rejects", async () => {
|
|
983
|
+
const data = createDtWithFiles(files);
|
|
984
|
+
|
|
985
|
+
const props = {
|
|
986
|
+
getFilesFromEvent: jest
|
|
987
|
+
.fn()
|
|
988
|
+
.mockImplementation(() => Promise.reject("oops :(")),
|
|
989
|
+
onDragEnter: jest.fn(),
|
|
990
|
+
onDrop: jest.fn(),
|
|
991
|
+
onError: jest.fn(),
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
const ui = (
|
|
995
|
+
<Dropzone {...props}>
|
|
996
|
+
{({ getRootProps, getInputProps }) => (
|
|
997
|
+
<div {...getRootProps()}>
|
|
998
|
+
<input {...getInputProps()} />
|
|
999
|
+
</div>
|
|
1000
|
+
)}
|
|
1001
|
+
</Dropzone>
|
|
1002
|
+
);
|
|
1003
|
+
const { container, rerender } = render(ui);
|
|
1004
|
+
const dropzone = container.querySelector("div");
|
|
1005
|
+
|
|
1006
|
+
fireDragEnter(dropzone, data);
|
|
1007
|
+
await flushPromises(rerender, ui);
|
|
1008
|
+
expect(props.onDragEnter).not.toHaveBeenCalled();
|
|
1009
|
+
|
|
1010
|
+
fireDrop(dropzone, data);
|
|
1011
|
+
await flushPromises(rerender, ui);
|
|
1012
|
+
expect(props.onDrop).not.toHaveBeenCalled();
|
|
1013
|
+
|
|
1014
|
+
expect(props.getFilesFromEvent).toHaveBeenCalledTimes(2);
|
|
1015
|
+
expect(props.onError).toHaveBeenCalledTimes(2);
|
|
1016
|
+
});
|
|
971
1017
|
});
|
|
972
1018
|
|
|
973
1019
|
describe("onFocus", () => {
|
|
@@ -1368,7 +1414,9 @@ describe("useDropzone() hook", () => {
|
|
|
1368
1414
|
<Dropzone
|
|
1369
1415
|
onDrop={onDropSpy}
|
|
1370
1416
|
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1371
|
-
accept=
|
|
1417
|
+
accept={{
|
|
1418
|
+
"application/pdf": [],
|
|
1419
|
+
}}
|
|
1372
1420
|
multiple
|
|
1373
1421
|
useFsAccessApi={false}
|
|
1374
1422
|
>
|
|
@@ -1425,7 +1473,9 @@ describe("useDropzone() hook", () => {
|
|
|
1425
1473
|
<Dropzone
|
|
1426
1474
|
onDrop={onDropSpy}
|
|
1427
1475
|
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1428
|
-
accept=
|
|
1476
|
+
accept={{
|
|
1477
|
+
"application/pdf": [],
|
|
1478
|
+
}}
|
|
1429
1479
|
multiple
|
|
1430
1480
|
>
|
|
1431
1481
|
{({ getRootProps, getInputProps, isFileDialogActive }) => (
|
|
@@ -1481,7 +1531,9 @@ describe("useDropzone() hook", () => {
|
|
|
1481
1531
|
<Dropzone
|
|
1482
1532
|
onDrop={onDropSpy}
|
|
1483
1533
|
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1484
|
-
accept=
|
|
1534
|
+
accept={{
|
|
1535
|
+
"application/pdf": [],
|
|
1536
|
+
}}
|
|
1485
1537
|
multiple
|
|
1486
1538
|
useFsAccessApi
|
|
1487
1539
|
>
|
|
@@ -1516,7 +1568,6 @@ describe("useDropzone() hook", () => {
|
|
|
1516
1568
|
multiple: true,
|
|
1517
1569
|
types: [
|
|
1518
1570
|
{
|
|
1519
|
-
description: "everything",
|
|
1520
1571
|
accept: { "application/pdf": [] },
|
|
1521
1572
|
},
|
|
1522
1573
|
],
|
|
@@ -1680,7 +1731,9 @@ describe("useDropzone() hook", () => {
|
|
|
1680
1731
|
<Dropzone
|
|
1681
1732
|
onDrop={onDropSpy}
|
|
1682
1733
|
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1683
|
-
accept=
|
|
1734
|
+
accept={{
|
|
1735
|
+
"application/pdf": [],
|
|
1736
|
+
}}
|
|
1684
1737
|
multiple
|
|
1685
1738
|
useFsAccessApi
|
|
1686
1739
|
>
|
|
@@ -1768,6 +1821,62 @@ describe("useDropzone() hook", () => {
|
|
|
1768
1821
|
|
|
1769
1822
|
jest.useRealTimers();
|
|
1770
1823
|
});
|
|
1824
|
+
|
|
1825
|
+
test("showOpenFilePicker() should call {onError} when an unexpected error occurs", async () => {
|
|
1826
|
+
const activeRef = createRef();
|
|
1827
|
+
const active = <span ref={activeRef}>I am active</span>;
|
|
1828
|
+
|
|
1829
|
+
const thenable = createThenable();
|
|
1830
|
+
const showOpenFilePickerMock = jest
|
|
1831
|
+
.fn()
|
|
1832
|
+
.mockReturnValue(thenable.promise);
|
|
1833
|
+
|
|
1834
|
+
window.showOpenFilePicker = showOpenFilePickerMock;
|
|
1835
|
+
|
|
1836
|
+
const onErrorSpy = jest.fn();
|
|
1837
|
+
const onDropSpy = jest.fn();
|
|
1838
|
+
const onFileDialogOpenSpy = jest.fn();
|
|
1839
|
+
|
|
1840
|
+
const ui = (
|
|
1841
|
+
<Dropzone
|
|
1842
|
+
onError={onErrorSpy}
|
|
1843
|
+
onDrop={onDropSpy}
|
|
1844
|
+
onFileDialogOpen={onFileDialogOpenSpy}
|
|
1845
|
+
accept={{
|
|
1846
|
+
"application/pdf": [],
|
|
1847
|
+
}}
|
|
1848
|
+
multiple
|
|
1849
|
+
useFsAccessApi
|
|
1850
|
+
>
|
|
1851
|
+
{({ getRootProps, getInputProps, isFileDialogActive }) => (
|
|
1852
|
+
<div {...getRootProps()}>
|
|
1853
|
+
<input {...getInputProps()} />
|
|
1854
|
+
{isFileDialogActive && active}
|
|
1855
|
+
</div>
|
|
1856
|
+
)}
|
|
1857
|
+
</Dropzone>
|
|
1858
|
+
);
|
|
1859
|
+
|
|
1860
|
+
const { container, rerender } = render(ui);
|
|
1861
|
+
|
|
1862
|
+
const dropzone = container.querySelector("div");
|
|
1863
|
+
|
|
1864
|
+
fireEvent.click(dropzone);
|
|
1865
|
+
|
|
1866
|
+
await flushPromises(rerender, ui);
|
|
1867
|
+
|
|
1868
|
+
expect(activeRef.current).not.toBeNull();
|
|
1869
|
+
expect(dropzone).toContainElement(activeRef.current);
|
|
1870
|
+
expect(onFileDialogOpenSpy).toHaveBeenCalled();
|
|
1871
|
+
|
|
1872
|
+
const err = new Error("oops :(");
|
|
1873
|
+
thenable.cancel(err);
|
|
1874
|
+
await flushPromises(rerender, ui);
|
|
1875
|
+
|
|
1876
|
+
expect(activeRef.current).not.toBeNull();
|
|
1877
|
+
expect(dropzone).toContainElement(activeRef.current);
|
|
1878
|
+
expect(onErrorSpy).toHaveBeenCalledWith(err);
|
|
1879
|
+
});
|
|
1771
1880
|
});
|
|
1772
1881
|
|
|
1773
1882
|
describe("onKeyDown", () => {
|
|
@@ -2215,7 +2324,11 @@ describe("useDropzone() hook", () => {
|
|
|
2215
2324
|
|
|
2216
2325
|
it("sets {isDragActive} and {isDragReject} of some files are not accepted on dragenter", async () => {
|
|
2217
2326
|
const ui = (
|
|
2218
|
-
<Dropzone
|
|
2327
|
+
<Dropzone
|
|
2328
|
+
accept={{
|
|
2329
|
+
"image/*": [],
|
|
2330
|
+
}}
|
|
2331
|
+
>
|
|
2219
2332
|
{({
|
|
2220
2333
|
getRootProps,
|
|
2221
2334
|
getInputProps,
|
|
@@ -2269,7 +2382,12 @@ describe("useDropzone() hook", () => {
|
|
|
2269
2382
|
|
|
2270
2383
|
it("sets {isDragActive, isDragAccept, isDragReject} if any files are rejected and {multiple} is false on dragenter", async () => {
|
|
2271
2384
|
const ui = (
|
|
2272
|
-
<Dropzone
|
|
2385
|
+
<Dropzone
|
|
2386
|
+
accept={{
|
|
2387
|
+
"image/*": [],
|
|
2388
|
+
}}
|
|
2389
|
+
multiple={false}
|
|
2390
|
+
>
|
|
2273
2391
|
{({
|
|
2274
2392
|
getRootProps,
|
|
2275
2393
|
getInputProps,
|
|
@@ -2337,7 +2455,11 @@ describe("useDropzone() hook", () => {
|
|
|
2337
2455
|
|
|
2338
2456
|
it("updates {isDragActive} if {accept} changes mid-drag", async () => {
|
|
2339
2457
|
const ui = (
|
|
2340
|
-
<Dropzone
|
|
2458
|
+
<Dropzone
|
|
2459
|
+
accept={{
|
|
2460
|
+
"image/*": [],
|
|
2461
|
+
}}
|
|
2462
|
+
>
|
|
2341
2463
|
{({
|
|
2342
2464
|
getRootProps,
|
|
2343
2465
|
getInputProps,
|
|
@@ -2366,7 +2488,11 @@ describe("useDropzone() hook", () => {
|
|
|
2366
2488
|
expect(dropzone).not.toHaveTextContent("dragReject");
|
|
2367
2489
|
|
|
2368
2490
|
rerender(
|
|
2369
|
-
<Dropzone
|
|
2491
|
+
<Dropzone
|
|
2492
|
+
accept={{
|
|
2493
|
+
"text/*": [],
|
|
2494
|
+
}}
|
|
2495
|
+
>
|
|
2370
2496
|
{({
|
|
2371
2497
|
getRootProps,
|
|
2372
2498
|
getInputProps,
|
|
@@ -2391,7 +2517,11 @@ describe("useDropzone() hook", () => {
|
|
|
2391
2517
|
|
|
2392
2518
|
it("resets {isDragActive, isDragAccept, isDragReject} on dragleave", async () => {
|
|
2393
2519
|
const ui = (
|
|
2394
|
-
<Dropzone
|
|
2520
|
+
<Dropzone
|
|
2521
|
+
accept={{
|
|
2522
|
+
"image/*": [],
|
|
2523
|
+
}}
|
|
2524
|
+
>
|
|
2395
2525
|
{({
|
|
2396
2526
|
getRootProps,
|
|
2397
2527
|
getInputProps,
|
|
@@ -2514,7 +2644,11 @@ describe("useDropzone() hook", () => {
|
|
|
2514
2644
|
Array.from(errorList).every((item) => item.textContent === code);
|
|
2515
2645
|
|
|
2516
2646
|
const ui = (
|
|
2517
|
-
<Dropzone
|
|
2647
|
+
<Dropzone
|
|
2648
|
+
accept={{
|
|
2649
|
+
"image/*": [],
|
|
2650
|
+
}}
|
|
2651
|
+
>
|
|
2518
2652
|
{({ getRootProps, getInputProps, acceptedFiles, fileRejections }) => (
|
|
2519
2653
|
<div {...getRootProps()}>
|
|
2520
2654
|
<input {...getInputProps()} />
|
|
@@ -2586,7 +2720,13 @@ describe("useDropzone() hook", () => {
|
|
|
2586
2720
|
it("rejects all files if {multiple} is false and {accept} criteria is not met", async () => {
|
|
2587
2721
|
const onDropSpy = jest.fn();
|
|
2588
2722
|
const ui = (
|
|
2589
|
-
<Dropzone
|
|
2723
|
+
<Dropzone
|
|
2724
|
+
accept={{
|
|
2725
|
+
"image/*": [],
|
|
2726
|
+
}}
|
|
2727
|
+
onDrop={onDropSpy}
|
|
2728
|
+
multiple={false}
|
|
2729
|
+
>
|
|
2590
2730
|
{({ getRootProps, getInputProps }) => (
|
|
2591
2731
|
<div {...getRootProps()}>
|
|
2592
2732
|
<input {...getInputProps()} />
|
|
@@ -2619,7 +2759,13 @@ describe("useDropzone() hook", () => {
|
|
|
2619
2759
|
it("rejects all files if {multiple} is false and {accept} criteria is met", async () => {
|
|
2620
2760
|
const onDropSpy = jest.fn();
|
|
2621
2761
|
const ui = (
|
|
2622
|
-
<Dropzone
|
|
2762
|
+
<Dropzone
|
|
2763
|
+
accept={{
|
|
2764
|
+
"image/*": [],
|
|
2765
|
+
}}
|
|
2766
|
+
onDrop={onDropSpy}
|
|
2767
|
+
multiple={false}
|
|
2768
|
+
>
|
|
2623
2769
|
{({ getRootProps, getInputProps }) => (
|
|
2624
2770
|
<div {...getRootProps()}>
|
|
2625
2771
|
<input {...getInputProps()} />
|
|
@@ -2663,7 +2809,9 @@ describe("useDropzone() hook", () => {
|
|
|
2663
2809
|
const onDropRejectedSpy = jest.fn();
|
|
2664
2810
|
const ui = (
|
|
2665
2811
|
<Dropzone
|
|
2666
|
-
accept=
|
|
2812
|
+
accept={{
|
|
2813
|
+
"image/*": [],
|
|
2814
|
+
}}
|
|
2667
2815
|
onDrop={onDropSpy}
|
|
2668
2816
|
onDropRejected={onDropRejectedSpy}
|
|
2669
2817
|
multiple={true}
|
|
@@ -2718,7 +2866,9 @@ describe("useDropzone() hook", () => {
|
|
|
2718
2866
|
const onDropRejectedSpy = jest.fn();
|
|
2719
2867
|
const ui = (maxFiles) => (
|
|
2720
2868
|
<Dropzone
|
|
2721
|
-
accept=
|
|
2869
|
+
accept={{
|
|
2870
|
+
"image/*": [],
|
|
2871
|
+
}}
|
|
2722
2872
|
onDrop={onDropSpy}
|
|
2723
2873
|
multiple={true}
|
|
2724
2874
|
maxFiles={maxFiles}
|
|
@@ -2758,7 +2908,9 @@ describe("useDropzone() hook", () => {
|
|
|
2758
2908
|
const onDropRejectedSpy = jest.fn();
|
|
2759
2909
|
const ui = (
|
|
2760
2910
|
<Dropzone
|
|
2761
|
-
accept=
|
|
2911
|
+
accept={{
|
|
2912
|
+
"image/*": [],
|
|
2913
|
+
}}
|
|
2762
2914
|
onDrop={onDropSpy}
|
|
2763
2915
|
multiple={true}
|
|
2764
2916
|
maxFiles={3}
|
|
@@ -2783,7 +2935,13 @@ describe("useDropzone() hook", () => {
|
|
|
2783
2935
|
it("accepts a single files if {multiple} is false and {accept} criteria is met", async () => {
|
|
2784
2936
|
const onDropSpy = jest.fn();
|
|
2785
2937
|
const ui = (
|
|
2786
|
-
<Dropzone
|
|
2938
|
+
<Dropzone
|
|
2939
|
+
accept={{
|
|
2940
|
+
"image/*": [],
|
|
2941
|
+
}}
|
|
2942
|
+
onDrop={onDropSpy}
|
|
2943
|
+
multiple={false}
|
|
2944
|
+
>
|
|
2787
2945
|
{({ getRootProps, getInputProps }) => (
|
|
2788
2946
|
<div {...getRootProps()}>
|
|
2789
2947
|
<input {...getInputProps()} />
|
|
@@ -2855,7 +3013,12 @@ describe("useDropzone() hook", () => {
|
|
|
2855
3013
|
it("gets invoked with both accepted/rejected files", async () => {
|
|
2856
3014
|
const onDropSpy = jest.fn();
|
|
2857
3015
|
const ui = (
|
|
2858
|
-
<Dropzone
|
|
3016
|
+
<Dropzone
|
|
3017
|
+
accept={{
|
|
3018
|
+
"image/*": [],
|
|
3019
|
+
}}
|
|
3020
|
+
onDrop={onDropSpy}
|
|
3021
|
+
>
|
|
2859
3022
|
{({ getRootProps, getInputProps }) => (
|
|
2860
3023
|
<div {...getRootProps()}>
|
|
2861
3024
|
<input {...getInputProps()} />
|
|
@@ -2912,7 +3075,12 @@ describe("useDropzone() hook", () => {
|
|
|
2912
3075
|
test("onDropAccepted callback is invoked if some files are accepted", async () => {
|
|
2913
3076
|
const onDropAcceptedSpy = jest.fn();
|
|
2914
3077
|
const ui = (
|
|
2915
|
-
<Dropzone
|
|
3078
|
+
<Dropzone
|
|
3079
|
+
accept={{
|
|
3080
|
+
"image/*": [],
|
|
3081
|
+
}}
|
|
3082
|
+
onDropAccepted={onDropAcceptedSpy}
|
|
3083
|
+
>
|
|
2916
3084
|
{({ getRootProps, getInputProps }) => (
|
|
2917
3085
|
<div {...getRootProps()}>
|
|
2918
3086
|
<input {...getInputProps()} />
|
|
@@ -2941,7 +3109,12 @@ describe("useDropzone() hook", () => {
|
|
|
2941
3109
|
test("onDropRejected callback is invoked if some files are rejected", async () => {
|
|
2942
3110
|
const onDropRejectedSpy = jest.fn();
|
|
2943
3111
|
const ui = (
|
|
2944
|
-
<Dropzone
|
|
3112
|
+
<Dropzone
|
|
3113
|
+
accept={{
|
|
3114
|
+
"image/*": [],
|
|
3115
|
+
}}
|
|
3116
|
+
onDropRejected={onDropRejectedSpy}
|
|
3117
|
+
>
|
|
2945
3118
|
{({ getRootProps, getInputProps }) => (
|
|
2946
3119
|
<div {...getRootProps()}>
|
|
2947
3120
|
<input {...getInputProps()} />
|
|
@@ -2996,7 +3169,12 @@ describe("useDropzone() hook", () => {
|
|
|
2996
3169
|
it("accepts a dropped image when Firefox provides a bogus file type", async () => {
|
|
2997
3170
|
const onDropSpy = jest.fn();
|
|
2998
3171
|
const ui = (
|
|
2999
|
-
<Dropzone
|
|
3172
|
+
<Dropzone
|
|
3173
|
+
accept={{
|
|
3174
|
+
"image/*": [],
|
|
3175
|
+
}}
|
|
3176
|
+
onDrop={onDropSpy}
|
|
3177
|
+
>
|
|
3000
3178
|
{({ getRootProps, getInputProps }) => (
|
|
3001
3179
|
<div {...getRootProps()}>
|
|
3002
3180
|
<input {...getInputProps()} />
|
package/src/utils/index.js
CHANGED
|
@@ -175,33 +175,60 @@ export function canUseFileSystemAccessAPI() {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* @param {
|
|
178
|
+
* Convert the `{accept}` dropzone prop to the
|
|
179
|
+
* `{types}` option for https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker
|
|
180
|
+
*
|
|
181
|
+
* @param {AcceptProp} accept
|
|
182
|
+
* @returns {{accept: string[]}[]}
|
|
182
183
|
*/
|
|
183
|
-
export function
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
184
|
+
export function pickerOptionsFromAccept(accept) {
|
|
185
|
+
if (isDefined(accept)) {
|
|
186
|
+
return Object.entries(accept)
|
|
187
|
+
.filter(([mimeType, ext]) => {
|
|
188
|
+
let ok = true;
|
|
189
|
+
|
|
190
|
+
if (!isMIMEType(mimeType)) {
|
|
191
|
+
console.warn(
|
|
192
|
+
`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.`
|
|
193
|
+
);
|
|
194
|
+
ok = false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!Array.isArray(ext) || !ext.every(isExt)) {
|
|
198
|
+
console.warn(
|
|
199
|
+
`Skipped "${mimeType}" because an invalid file extension was provided.`
|
|
200
|
+
);
|
|
201
|
+
ok = false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return ok;
|
|
205
|
+
})
|
|
206
|
+
.map(([mimeType, ext]) => ({
|
|
207
|
+
accept: {
|
|
208
|
+
[mimeType]: ext,
|
|
209
|
+
},
|
|
210
|
+
}));
|
|
211
|
+
}
|
|
212
|
+
return accept;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Convert the `{accept}` dropzone prop to an array of MIME types/extensions.
|
|
217
|
+
* @param {AcceptProp} accept
|
|
218
|
+
* @returns {string}
|
|
219
|
+
*/
|
|
220
|
+
export function acceptPropAsAcceptAttr(accept) {
|
|
221
|
+
if (isDefined(accept)) {
|
|
222
|
+
return (
|
|
223
|
+
Object.entries(accept)
|
|
224
|
+
.reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], [])
|
|
225
|
+
// Silently discard invalid entries as pickerOptionsFromAccept warns about these
|
|
226
|
+
.filter((v) => isMIMEType(v) || isExt(v))
|
|
227
|
+
.join(",")
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return undefined;
|
|
205
232
|
}
|
|
206
233
|
|
|
207
234
|
/**
|
|
@@ -231,3 +258,32 @@ export function isSecurityError(v) {
|
|
|
231
258
|
(v.name === "SecurityError" || v.code === v.SECURITY_ERR)
|
|
232
259
|
);
|
|
233
260
|
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Check if v is a MIME type string.
|
|
264
|
+
*
|
|
265
|
+
* See accepted format: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#unique_file_type_specifiers.
|
|
266
|
+
*
|
|
267
|
+
* @param {string} v
|
|
268
|
+
*/
|
|
269
|
+
export function isMIMEType(v) {
|
|
270
|
+
return (
|
|
271
|
+
v === "audio/*" ||
|
|
272
|
+
v === "video/*" ||
|
|
273
|
+
v === "image/*" ||
|
|
274
|
+
v === "text/*" ||
|
|
275
|
+
/\w+\/[-+.\w]+/g.test(v)
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Check if v is a file extension.
|
|
281
|
+
* @param {string} v
|
|
282
|
+
*/
|
|
283
|
+
export function isExt(v) {
|
|
284
|
+
return /^.*\.[\w]+$/.test(v);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @typedef {Object.<string, string[]>} AcceptProp
|
|
289
|
+
*/
|