@webiny/app-admin 6.4.0-beta.0 → 6.4.0-beta.1
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/package.json +23 -21
- package/presentation/browserFilePicker/BrowserFilePicker.d.ts +3 -0
- package/presentation/browserFilePicker/BrowserFilePicker.js +88 -0
- package/presentation/browserFilePicker/BrowserFilePicker.js.map +1 -0
- package/presentation/browserFilePicker/BrowserFilePickerPresenter.d.ts +10 -0
- package/presentation/browserFilePicker/BrowserFilePickerPresenter.js +84 -0
- package/presentation/browserFilePicker/BrowserFilePickerPresenter.js.map +1 -0
- package/presentation/browserFilePicker/index.d.ts +3 -0
- package/presentation/browserFilePicker/index.js +2 -0
- package/presentation/browserFilePicker/types.d.ts +53 -0
- package/presentation/browserFilePicker/types.js +0 -0
- package/presentation/browserFilePicker/utils.d.ts +2 -0
- package/presentation/browserFilePicker/utils.js +16 -0
- package/presentation/browserFilePicker/utils.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/app-admin",
|
|
3
|
-
"version": "6.4.0-beta.
|
|
3
|
+
"version": "6.4.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -21,31 +21,32 @@
|
|
|
21
21
|
"@emotion/styled": "11.14.1",
|
|
22
22
|
"@fortawesome/fontawesome-svg-core": "7.2.0",
|
|
23
23
|
"@fortawesome/react-fontawesome": "3.3.1",
|
|
24
|
-
"@iconify/json": "2.2.
|
|
24
|
+
"@iconify/json": "2.2.474",
|
|
25
25
|
"@svgr/webpack": "8.1.0",
|
|
26
26
|
"@types/react": "18.3.28",
|
|
27
|
-
"@webiny/admin-ui": "6.4.0-beta.
|
|
28
|
-
"@webiny/app": "6.4.0-beta.
|
|
29
|
-
"@webiny/di": "0.
|
|
30
|
-
"@webiny/feature": "6.4.0-beta.
|
|
31
|
-
"@webiny/form": "6.4.0-beta.
|
|
32
|
-
"@webiny/icons": "6.4.0-beta.
|
|
33
|
-
"@webiny/lexical-converter": "6.4.0-beta.
|
|
34
|
-
"@webiny/lexical-editor": "6.4.0-beta.
|
|
35
|
-
"@webiny/lexical-theme": "6.4.0-beta.
|
|
36
|
-
"@webiny/plugins": "6.4.0-beta.
|
|
37
|
-
"@webiny/react-composition": "6.4.0-beta.
|
|
38
|
-
"@webiny/react-properties": "6.4.0-beta.
|
|
39
|
-
"@webiny/sdk": "6.4.0-beta.
|
|
40
|
-
"@webiny/telemetry": "6.4.0-beta.
|
|
41
|
-
"@webiny/utils": "6.4.0-beta.
|
|
42
|
-
"@webiny/validation": "6.4.0-beta.
|
|
43
|
-
"@webiny/wcp": "6.4.0-beta.
|
|
27
|
+
"@webiny/admin-ui": "6.4.0-beta.1",
|
|
28
|
+
"@webiny/app": "6.4.0-beta.1",
|
|
29
|
+
"@webiny/di": "1.0.0",
|
|
30
|
+
"@webiny/feature": "6.4.0-beta.1",
|
|
31
|
+
"@webiny/form": "6.4.0-beta.1",
|
|
32
|
+
"@webiny/icons": "6.4.0-beta.1",
|
|
33
|
+
"@webiny/lexical-converter": "6.4.0-beta.1",
|
|
34
|
+
"@webiny/lexical-editor": "6.4.0-beta.1",
|
|
35
|
+
"@webiny/lexical-theme": "6.4.0-beta.1",
|
|
36
|
+
"@webiny/plugins": "6.4.0-beta.1",
|
|
37
|
+
"@webiny/react-composition": "6.4.0-beta.1",
|
|
38
|
+
"@webiny/react-properties": "6.4.0-beta.1",
|
|
39
|
+
"@webiny/sdk": "6.4.0-beta.1",
|
|
40
|
+
"@webiny/telemetry": "6.4.0-beta.1",
|
|
41
|
+
"@webiny/utils": "6.4.0-beta.1",
|
|
42
|
+
"@webiny/validation": "6.4.0-beta.1",
|
|
43
|
+
"@webiny/wcp": "6.4.0-beta.1",
|
|
44
44
|
"apollo-cache": "1.3.5",
|
|
45
45
|
"apollo-client": "2.6.10",
|
|
46
46
|
"apollo-link": "1.2.14",
|
|
47
47
|
"apollo-link-context": "1.0.20",
|
|
48
48
|
"apollo-utilities": "1.3.4",
|
|
49
|
+
"bytes": "3.1.2",
|
|
49
50
|
"classnames": "2.5.1",
|
|
50
51
|
"graphql": "16.14.0",
|
|
51
52
|
"graphql-tag": "2.12.6",
|
|
@@ -54,6 +55,7 @@
|
|
|
54
55
|
"lexical": "0.44.0",
|
|
55
56
|
"lodash": "4.18.1",
|
|
56
57
|
"markdown-to-jsx": "9.8.0",
|
|
58
|
+
"mime": "4.1.0",
|
|
57
59
|
"minimatch": "10.2.5",
|
|
58
60
|
"mobx": "6.15.3",
|
|
59
61
|
"mobx-react-lite": "4.1.1",
|
|
@@ -77,7 +79,7 @@
|
|
|
77
79
|
"@types/react-transition-group": "4.4.12",
|
|
78
80
|
"@types/store": "2.0.5",
|
|
79
81
|
"@types/tinycolor2": "1.4.6",
|
|
80
|
-
"@webiny/build-tools": "6.4.0-beta.
|
|
82
|
+
"@webiny/build-tools": "6.4.0-beta.1",
|
|
81
83
|
"rimraf": "6.1.3",
|
|
82
84
|
"typescript": "6.0.3",
|
|
83
85
|
"vitest": "4.1.6"
|
|
@@ -103,5 +105,5 @@
|
|
|
103
105
|
]
|
|
104
106
|
}
|
|
105
107
|
},
|
|
106
|
-
"gitHead": "
|
|
108
|
+
"gitHead": "73237b8243693038c072bae1c0b783387448cbbe"
|
|
107
109
|
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { BrowserFilePickerProps } from "./types.js";
|
|
3
|
+
export declare function BrowserFilePicker({ accept, multiple, maxSize, multipleMaxSize, multipleMaxCount, convertToBase64, onSuccess, onError, children, id: externalId }: BrowserFilePickerProps): React.JSX.Element;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import react, { useCallback, useId, useMemo, useRef } from "react";
|
|
2
|
+
import { BrowserFilePickerPresenter } from "./BrowserFilePickerPresenter.js";
|
|
3
|
+
function BrowserFilePicker({ accept = [], multiple = false, maxSize = "2mb", multipleMaxSize = "10mb", multipleMaxCount = null, convertToBase64 = false, onSuccess, onError, children, id: externalId }) {
|
|
4
|
+
const generatedId = useId();
|
|
5
|
+
const inputId = externalId ?? generatedId;
|
|
6
|
+
const inputRef = useRef(null);
|
|
7
|
+
const browseCallbacksRef = useRef(null);
|
|
8
|
+
const presenter = useMemo(()=>new BrowserFilePickerPresenter({
|
|
9
|
+
accept,
|
|
10
|
+
multiple,
|
|
11
|
+
maxSize,
|
|
12
|
+
multipleMaxSize,
|
|
13
|
+
multipleMaxCount,
|
|
14
|
+
convertToBase64
|
|
15
|
+
}), [
|
|
16
|
+
accept,
|
|
17
|
+
multiple,
|
|
18
|
+
maxSize,
|
|
19
|
+
multipleMaxSize,
|
|
20
|
+
multipleMaxCount,
|
|
21
|
+
convertToBase64
|
|
22
|
+
]);
|
|
23
|
+
const resolveCallbacks = useCallback(()=>{
|
|
24
|
+
const overrides = browseCallbacksRef.current;
|
|
25
|
+
return {
|
|
26
|
+
onSuccess: overrides?.onSuccess ?? onSuccess,
|
|
27
|
+
onError: overrides?.onError ?? onError
|
|
28
|
+
};
|
|
29
|
+
}, [
|
|
30
|
+
onSuccess,
|
|
31
|
+
onError
|
|
32
|
+
]);
|
|
33
|
+
const handleFiles = useCallback(async (files)=>{
|
|
34
|
+
await presenter.processFiles(files, resolveCallbacks());
|
|
35
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
36
|
+
browseCallbacksRef.current = null;
|
|
37
|
+
}, [
|
|
38
|
+
presenter,
|
|
39
|
+
resolveCallbacks
|
|
40
|
+
]);
|
|
41
|
+
const renderProps = useMemo(()=>({
|
|
42
|
+
browseFiles: (params)=>{
|
|
43
|
+
browseCallbacksRef.current = params ?? null;
|
|
44
|
+
inputRef.current?.click();
|
|
45
|
+
},
|
|
46
|
+
getDropZoneProps: (additionalProps = {})=>{
|
|
47
|
+
const { onDragOver, onDrop, ...rest } = additionalProps;
|
|
48
|
+
return {
|
|
49
|
+
...rest,
|
|
50
|
+
onDragOver: (e)=>{
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
"function" == typeof onDragOver && onDragOver(e);
|
|
53
|
+
},
|
|
54
|
+
onDrop: async (e)=>{
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
"function" == typeof onDrop && onDrop(e);
|
|
57
|
+
if (e.dataTransfer?.files) await handleFiles(Array.from(e.dataTransfer.files));
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
getLabelProps: (additionalProps = {})=>({
|
|
62
|
+
...additionalProps,
|
|
63
|
+
htmlFor: inputId
|
|
64
|
+
}),
|
|
65
|
+
validateFiles: (files)=>presenter.validateFiles(files)
|
|
66
|
+
}), [
|
|
67
|
+
inputId,
|
|
68
|
+
presenter,
|
|
69
|
+
handleFiles
|
|
70
|
+
]);
|
|
71
|
+
return /*#__PURE__*/ react.createElement(react.Fragment, null, children(renderProps), /*#__PURE__*/ react.createElement("input", {
|
|
72
|
+
id: inputId,
|
|
73
|
+
ref: inputRef,
|
|
74
|
+
accept: accept.join(","),
|
|
75
|
+
style: {
|
|
76
|
+
display: "none"
|
|
77
|
+
},
|
|
78
|
+
type: "file",
|
|
79
|
+
multiple: multiple,
|
|
80
|
+
onChange: (e)=>{
|
|
81
|
+
const files = e.target.files;
|
|
82
|
+
if (files) handleFiles(Array.from(files));
|
|
83
|
+
}
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
export { BrowserFilePicker };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=BrowserFilePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/browserFilePicker/BrowserFilePicker.js","sources":["../../../src/presentation/browserFilePicker/BrowserFilePicker.tsx"],"sourcesContent":["import React, { useCallback, useId, useMemo, useRef } from \"react\";\nimport { BrowserFilePickerPresenter } from \"./BrowserFilePickerPresenter.js\";\nimport type {\n BrowserFilePickerProps,\n BrowserFilePickerRenderProps,\n BrowseFilesParams\n} from \"./types.js\";\n\nexport function BrowserFilePicker({\n accept = [],\n multiple = false,\n maxSize = \"2mb\",\n multipleMaxSize = \"10mb\",\n multipleMaxCount = null,\n convertToBase64 = false,\n onSuccess,\n onError,\n children,\n id: externalId\n}: BrowserFilePickerProps) {\n const generatedId = useId();\n const inputId = externalId ?? generatedId;\n const inputRef = useRef<HTMLInputElement>(null);\n const browseCallbacksRef = useRef<BrowseFilesParams | null>(null);\n\n const presenter = useMemo(() => {\n return new BrowserFilePickerPresenter({\n accept,\n multiple,\n maxSize,\n multipleMaxSize,\n multipleMaxCount,\n convertToBase64\n });\n }, [accept, multiple, maxSize, multipleMaxSize, multipleMaxCount, convertToBase64]);\n\n const resolveCallbacks = useCallback((): BrowseFilesParams => {\n const overrides = browseCallbacksRef.current;\n return {\n onSuccess: overrides?.onSuccess ?? onSuccess,\n onError: overrides?.onError ?? onError\n };\n }, [onSuccess, onError]);\n\n const handleFiles = useCallback(\n async (files: File[]) => {\n await presenter.processFiles(files, resolveCallbacks());\n\n if (inputRef.current) {\n inputRef.current.value = \"\";\n }\n browseCallbacksRef.current = null;\n },\n [presenter, resolveCallbacks]\n );\n\n const renderProps: BrowserFilePickerRenderProps = useMemo(\n () => ({\n browseFiles: (params?: BrowseFilesParams) => {\n browseCallbacksRef.current = params ?? null;\n inputRef.current?.click();\n },\n\n getDropZoneProps: (additionalProps: Record<string, any> = {}) => {\n const { onDragOver, onDrop, ...rest } = additionalProps;\n return {\n ...rest,\n onDragOver: (e: DragEvent) => {\n e.preventDefault();\n typeof onDragOver === \"function\" && onDragOver(e);\n },\n onDrop: async (e: DragEvent) => {\n e.preventDefault();\n typeof onDrop === \"function\" && onDrop(e);\n if (e.dataTransfer?.files) {\n await handleFiles(Array.from(e.dataTransfer.files));\n }\n }\n };\n },\n\n getLabelProps: (additionalProps: Record<string, any> = {}) => ({\n ...additionalProps,\n htmlFor: inputId\n }),\n\n validateFiles: (files: Array<{ type: string; size: number }>) =>\n presenter.validateFiles(files)\n }),\n [inputId, presenter, handleFiles]\n );\n\n return (\n <>\n {children(renderProps)}\n <input\n id={inputId}\n ref={inputRef}\n accept={accept.join(\",\")}\n style={{ display: \"none\" }}\n type=\"file\"\n multiple={multiple}\n onChange={e => {\n const files = e.target.files;\n if (files) {\n void handleFiles(Array.from(files));\n }\n }}\n />\n </>\n );\n}\n"],"names":["BrowserFilePicker","accept","multiple","maxSize","multipleMaxSize","multipleMaxCount","convertToBase64","onSuccess","onError","children","externalId","generatedId","useId","inputId","inputRef","useRef","browseCallbacksRef","presenter","useMemo","BrowserFilePickerPresenter","resolveCallbacks","useCallback","overrides","handleFiles","files","renderProps","params","additionalProps","onDragOver","onDrop","rest","e","Array"],"mappings":";;AAQO,SAASA,kBAAkB,EAC9BC,SAAS,EAAE,EACXC,WAAW,KAAK,EAChBC,UAAU,KAAK,EACfC,kBAAkB,MAAM,EACxBC,mBAAmB,IAAI,EACvBC,kBAAkB,KAAK,EACvBC,SAAS,EACTC,OAAO,EACPC,QAAQ,EACR,IAAIC,UAAU,EACO;IACrB,MAAMC,cAAcC;IACpB,MAAMC,UAAUH,cAAcC;IAC9B,MAAMG,WAAWC,OAAyB;IAC1C,MAAMC,qBAAqBD,OAAiC;IAE5D,MAAME,YAAYC,QAAQ,IACf,IAAIC,2BAA2B;YAClClB;YACAC;YACAC;YACAC;YACAC;YACAC;QACJ,IACD;QAACL;QAAQC;QAAUC;QAASC;QAAiBC;QAAkBC;KAAgB;IAElF,MAAMc,mBAAmBC,YAAY;QACjC,MAAMC,YAAYN,mBAAmB,OAAO;QAC5C,OAAO;YACH,WAAWM,WAAW,aAAaf;YACnC,SAASe,WAAW,WAAWd;QACnC;IACJ,GAAG;QAACD;QAAWC;KAAQ;IAEvB,MAAMe,cAAcF,YAChB,OAAOG;QACH,MAAMP,UAAU,YAAY,CAACO,OAAOJ;QAEpC,IAAIN,SAAS,OAAO,EAChBA,SAAS,OAAO,CAAC,KAAK,GAAG;QAE7BE,mBAAmB,OAAO,GAAG;IACjC,GACA;QAACC;QAAWG;KAAiB;IAGjC,MAAMK,cAA4CP,QAC9C,IAAO;YACH,aAAa,CAACQ;gBACVV,mBAAmB,OAAO,GAAGU,UAAU;gBACvCZ,SAAS,OAAO,EAAE;YACtB;YAEA,kBAAkB,CAACa,kBAAuC,CAAC,CAAC;gBACxD,MAAM,EAAEC,UAAU,EAAEC,MAAM,EAAE,GAAGC,MAAM,GAAGH;gBACxC,OAAO;oBACH,GAAGG,IAAI;oBACP,YAAY,CAACC;wBACTA,EAAE,cAAc;wBACM,cAAtB,OAAOH,cAA6BA,WAAWG;oBACnD;oBACA,QAAQ,OAAOA;wBACXA,EAAE,cAAc;wBACE,cAAlB,OAAOF,UAAyBA,OAAOE;wBACvC,IAAIA,EAAE,YAAY,EAAE,OAChB,MAAMR,YAAYS,MAAM,IAAI,CAACD,EAAE,YAAY,CAAC,KAAK;oBAEzD;gBACJ;YACJ;YAEA,eAAe,CAACJ,kBAAuC,CAAC,CAAC,GAAM;oBAC3D,GAAGA,eAAe;oBAClB,SAASd;gBACb;YAEA,eAAe,CAACW,QACZP,UAAU,aAAa,CAACO;QAChC,IACA;QAACX;QAASI;QAAWM;KAAY;IAGrC,OAAO,WAAP,GACI,0CACKd,SAASgB,cAAAA,WAAAA,GACV,oBAAC;QACG,IAAIZ;QACJ,KAAKC;QACL,QAAQb,OAAO,IAAI,CAAC;QACpB,OAAO;YAAE,SAAS;QAAO;QACzB,MAAK;QACL,UAAUC;QACV,UAAU6B,CAAAA;YACN,MAAMP,QAAQO,EAAE,MAAM,CAAC,KAAK;YAC5B,IAAIP,OACKD,YAAYS,MAAM,IAAI,CAACR;QAEpC;;AAIhB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BrowserFilePickerConfig, FileError, BrowseFilesParams } from "./types.js";
|
|
2
|
+
export declare class BrowserFilePickerPresenter {
|
|
3
|
+
private readonly config;
|
|
4
|
+
constructor(config: BrowserFilePickerConfig);
|
|
5
|
+
validateFiles(files: Array<{
|
|
6
|
+
type: string;
|
|
7
|
+
size: number;
|
|
8
|
+
}>): FileError[];
|
|
9
|
+
processFiles(rawFiles: File[], callbacks: BrowseFilesParams): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import bytes from "bytes";
|
|
2
|
+
import { minimatch } from "minimatch";
|
|
3
|
+
import { generateId, readFileContent } from "./utils.js";
|
|
4
|
+
async function resolveTypeFromName(fileName) {
|
|
5
|
+
const mime = await import("mime");
|
|
6
|
+
return mime.default.getType(fileName) || "";
|
|
7
|
+
}
|
|
8
|
+
class BrowserFilePickerPresenter {
|
|
9
|
+
constructor(config){
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
validateFiles(files) {
|
|
13
|
+
const { multiple, multipleMaxSize, multipleMaxCount, accept, maxSize } = this.config;
|
|
14
|
+
const errors = [];
|
|
15
|
+
let multipleFileSize = 0;
|
|
16
|
+
if (!multiple && files.length > 1) {
|
|
17
|
+
errors.push({
|
|
18
|
+
id: generateId(),
|
|
19
|
+
type: "multipleNotAllowed"
|
|
20
|
+
});
|
|
21
|
+
return errors;
|
|
22
|
+
}
|
|
23
|
+
for(let index = 0; index < files.length; index++){
|
|
24
|
+
const file = files[index];
|
|
25
|
+
if (Array.isArray(accept) && accept.length && !accept.some((type)=>minimatch(file.type, type))) errors.push({
|
|
26
|
+
id: generateId(),
|
|
27
|
+
index,
|
|
28
|
+
file: file,
|
|
29
|
+
type: "unsupportedFileType"
|
|
30
|
+
});
|
|
31
|
+
else if (maxSize) {
|
|
32
|
+
const sizeAsBytes = bytes(maxSize);
|
|
33
|
+
if (sizeAsBytes && file.size > sizeAsBytes) errors.push({
|
|
34
|
+
id: generateId(),
|
|
35
|
+
index,
|
|
36
|
+
file: file,
|
|
37
|
+
type: "maxSizeExceeded"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (multiple) multipleFileSize += file.size;
|
|
41
|
+
}
|
|
42
|
+
if (multiple) {
|
|
43
|
+
const maxMultipleMaxSize = bytes(multipleMaxSize);
|
|
44
|
+
if (maxMultipleMaxSize && multipleMaxSize && multipleFileSize > maxMultipleMaxSize) errors.push({
|
|
45
|
+
id: generateId(),
|
|
46
|
+
type: "multipleMaxSizeExceeded",
|
|
47
|
+
multipleFileSize,
|
|
48
|
+
multipleMaxSize: maxMultipleMaxSize
|
|
49
|
+
});
|
|
50
|
+
if (multipleMaxCount && files.length > multipleMaxCount) errors.push({
|
|
51
|
+
id: generateId(),
|
|
52
|
+
type: "multipleMaxCountExceeded",
|
|
53
|
+
multipleCount: files.length,
|
|
54
|
+
multipleMaxCount
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return errors;
|
|
58
|
+
}
|
|
59
|
+
async processFiles(rawFiles, callbacks) {
|
|
60
|
+
if (0 === rawFiles.length) return;
|
|
61
|
+
const files = await Promise.all([
|
|
62
|
+
...rawFiles
|
|
63
|
+
].map(async (file)=>{
|
|
64
|
+
const mimeType = "" !== file.type ? file.type : await resolveTypeFromName(file.name);
|
|
65
|
+
return {
|
|
66
|
+
id: generateId(),
|
|
67
|
+
name: file.name,
|
|
68
|
+
type: mimeType,
|
|
69
|
+
size: file.size,
|
|
70
|
+
src: {
|
|
71
|
+
file,
|
|
72
|
+
base64: null
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}));
|
|
76
|
+
const errors = this.validateFiles(files);
|
|
77
|
+
if (errors.length && callbacks.onError) return void callbacks.onError(errors, files);
|
|
78
|
+
if (this.config.convertToBase64) for(let i = 0; i < files.length; i++)files[i].src.base64 = await readFileContent(files[i].src.file);
|
|
79
|
+
if (callbacks.onSuccess) callbacks.onSuccess(files);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export { BrowserFilePickerPresenter };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=BrowserFilePickerPresenter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/browserFilePicker/BrowserFilePickerPresenter.js","sources":["../../../src/presentation/browserFilePicker/BrowserFilePickerPresenter.ts"],"sourcesContent":["import bytes from \"bytes\";\nimport { minimatch } from \"minimatch\";\nimport { generateId, readFileContent } from \"./utils.js\";\nimport type {\n BrowserFilePickerConfig,\n SelectedFile,\n FileError,\n BrowseFilesParams\n} from \"./types.js\";\n\nasync function resolveTypeFromName(fileName: string): Promise<string> {\n const mime = await import(\"mime\");\n return mime.default.getType(fileName) || \"\";\n}\n\nexport class BrowserFilePickerPresenter {\n private readonly config: BrowserFilePickerConfig;\n\n constructor(config: BrowserFilePickerConfig) {\n this.config = config;\n }\n\n validateFiles(files: Array<{ type: string; size: number }>): FileError[] {\n const { multiple, multipleMaxSize, multipleMaxCount, accept, maxSize } = this.config;\n const errors: FileError[] = [];\n let multipleFileSize = 0;\n\n if (!multiple && files.length > 1) {\n errors.push({ id: generateId(), type: \"multipleNotAllowed\" });\n return errors;\n }\n\n for (let index = 0; index < files.length; index++) {\n const file = files[index];\n\n if (\n Array.isArray(accept) &&\n accept.length &&\n !accept.some(type => minimatch(file.type, type))\n ) {\n errors.push({\n id: generateId(),\n index,\n file: file as SelectedFile | File,\n type: \"unsupportedFileType\"\n });\n } else if (maxSize) {\n const sizeAsBytes = bytes(maxSize);\n if (sizeAsBytes && file.size > sizeAsBytes) {\n errors.push({\n id: generateId(),\n index,\n file: file as SelectedFile | File,\n type: \"maxSizeExceeded\"\n });\n }\n }\n\n if (multiple) {\n multipleFileSize += file.size;\n }\n }\n\n if (multiple) {\n const maxMultipleMaxSize = bytes(multipleMaxSize);\n\n if (maxMultipleMaxSize && multipleMaxSize && multipleFileSize > maxMultipleMaxSize) {\n errors.push({\n id: generateId(),\n type: \"multipleMaxSizeExceeded\",\n multipleFileSize,\n multipleMaxSize: maxMultipleMaxSize\n });\n }\n\n if (multipleMaxCount && files.length > multipleMaxCount) {\n errors.push({\n id: generateId(),\n type: \"multipleMaxCountExceeded\",\n multipleCount: files.length,\n multipleMaxCount\n });\n }\n }\n\n return errors;\n }\n\n async processFiles(rawFiles: File[], callbacks: BrowseFilesParams): Promise<void> {\n if (rawFiles.length === 0) {\n return;\n }\n\n const files: SelectedFile[] = await Promise.all(\n [...rawFiles].map(async file => {\n const mimeType =\n file.type !== \"\" ? file.type : await resolveTypeFromName(file.name);\n\n return {\n id: generateId(),\n name: file.name,\n type: mimeType,\n size: file.size,\n src: { file, base64: null }\n };\n })\n );\n\n const errors = this.validateFiles(files);\n\n if (errors.length && callbacks.onError) {\n callbacks.onError(errors, files);\n return;\n }\n\n if (this.config.convertToBase64) {\n for (let i = 0; i < files.length; i++) {\n files[i].src.base64 = await readFileContent(files[i].src.file);\n }\n }\n\n if (callbacks.onSuccess) {\n callbacks.onSuccess(files);\n }\n }\n}\n"],"names":["resolveTypeFromName","fileName","mime","BrowserFilePickerPresenter","config","files","multiple","multipleMaxSize","multipleMaxCount","accept","maxSize","errors","multipleFileSize","generateId","index","file","Array","type","minimatch","sizeAsBytes","bytes","maxMultipleMaxSize","rawFiles","callbacks","Promise","mimeType","i","readFileContent"],"mappings":";;;AAUA,eAAeA,oBAAoBC,QAAgB;IAC/C,MAAMC,OAAO,MAAM,MAAM,CAAC;IAC1B,OAAOA,KAAK,OAAO,CAAC,OAAO,CAACD,aAAa;AAC7C;AAEO,MAAME;IAGT,YAAYC,MAA+B,CAAE;QACzC,IAAI,CAAC,MAAM,GAAGA;IAClB;IAEA,cAAcC,KAA4C,EAAe;QACrE,MAAM,EAAEC,QAAQ,EAAEC,eAAe,EAAEC,gBAAgB,EAAEC,MAAM,EAAEC,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM;QACpF,MAAMC,SAAsB,EAAE;QAC9B,IAAIC,mBAAmB;QAEvB,IAAI,CAACN,YAAYD,MAAM,MAAM,GAAG,GAAG;YAC/BM,OAAO,IAAI,CAAC;gBAAE,IAAIE;gBAAc,MAAM;YAAqB;YAC3D,OAAOF;QACX;QAEA,IAAK,IAAIG,QAAQ,GAAGA,QAAQT,MAAM,MAAM,EAAES,QAAS;YAC/C,MAAMC,OAAOV,KAAK,CAACS,MAAM;YAEzB,IACIE,MAAM,OAAO,CAACP,WACdA,OAAO,MAAM,IACb,CAACA,OAAO,IAAI,CAACQ,CAAAA,OAAQC,UAAUH,KAAK,IAAI,EAAEE,QAE1CN,OAAO,IAAI,CAAC;gBACR,IAAIE;gBACJC;gBACA,MAAMC;gBACN,MAAM;YACV;iBACG,IAAIL,SAAS;gBAChB,MAAMS,cAAcC,MAAMV;gBAC1B,IAAIS,eAAeJ,KAAK,IAAI,GAAGI,aAC3BR,OAAO,IAAI,CAAC;oBACR,IAAIE;oBACJC;oBACA,MAAMC;oBACN,MAAM;gBACV;YAER;YAEA,IAAIT,UACAM,oBAAoBG,KAAK,IAAI;QAErC;QAEA,IAAIT,UAAU;YACV,MAAMe,qBAAqBD,MAAMb;YAEjC,IAAIc,sBAAsBd,mBAAmBK,mBAAmBS,oBAC5DV,OAAO,IAAI,CAAC;gBACR,IAAIE;gBACJ,MAAM;gBACND;gBACA,iBAAiBS;YACrB;YAGJ,IAAIb,oBAAoBH,MAAM,MAAM,GAAGG,kBACnCG,OAAO,IAAI,CAAC;gBACR,IAAIE;gBACJ,MAAM;gBACN,eAAeR,MAAM,MAAM;gBAC3BG;YACJ;QAER;QAEA,OAAOG;IACX;IAEA,MAAM,aAAaW,QAAgB,EAAEC,SAA4B,EAAiB;QAC9E,IAAID,AAAoB,MAApBA,SAAS,MAAM,EACf;QAGJ,MAAMjB,QAAwB,MAAMmB,QAAQ,GAAG,CAC3C;eAAIF;SAAS,CAAC,GAAG,CAAC,OAAMP;YACpB,MAAMU,WACFV,AAAc,OAAdA,KAAK,IAAI,GAAUA,KAAK,IAAI,GAAG,MAAMf,oBAAoBe,KAAK,IAAI;YAEtE,OAAO;gBACH,IAAIF;gBACJ,MAAME,KAAK,IAAI;gBACf,MAAMU;gBACN,MAAMV,KAAK,IAAI;gBACf,KAAK;oBAAEA;oBAAM,QAAQ;gBAAK;YAC9B;QACJ;QAGJ,MAAMJ,SAAS,IAAI,CAAC,aAAa,CAACN;QAElC,IAAIM,OAAO,MAAM,IAAIY,UAAU,OAAO,EAAE,YACpCA,UAAU,OAAO,CAACZ,QAAQN;QAI9B,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,EAC3B,IAAK,IAAIqB,IAAI,GAAGA,IAAIrB,MAAM,MAAM,EAAEqB,IAC9BrB,KAAK,CAACqB,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,MAAMC,gBAAgBtB,KAAK,CAACqB,EAAE,CAAC,GAAG,CAAC,IAAI;QAIrE,IAAIH,UAAU,SAAS,EACnBA,UAAU,SAAS,CAAClB;IAE5B;AACJ"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { BrowserFilePicker } from "./BrowserFilePicker.js";
|
|
2
|
+
export { BrowserFilePickerPresenter } from "./BrowserFilePickerPresenter.js";
|
|
3
|
+
export type { SelectedFile, FileError, BrowseFilesParams, BrowserFilePickerRenderProps, BrowserFilePickerConfig, BrowserFilePickerProps } from "./types.js";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export interface SelectedFile {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
type: string;
|
|
5
|
+
size: number;
|
|
6
|
+
src: {
|
|
7
|
+
file: File;
|
|
8
|
+
base64: string | null;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface FileError {
|
|
12
|
+
id: string;
|
|
13
|
+
type: "unsupportedFileType" | "maxSizeExceeded" | "multipleMaxSizeExceeded" | "multipleMaxCountExceeded" | "multipleNotAllowed";
|
|
14
|
+
index?: number;
|
|
15
|
+
file?: SelectedFile | File;
|
|
16
|
+
multipleFileSize?: number;
|
|
17
|
+
multipleMaxSize?: number;
|
|
18
|
+
multipleMaxCount?: number;
|
|
19
|
+
multipleCount?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface BrowseFilesParams {
|
|
22
|
+
onSuccess?: (files: SelectedFile[]) => void;
|
|
23
|
+
onError?: (errors: FileError[], files: SelectedFile[]) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface BrowserFilePickerRenderProps {
|
|
26
|
+
browseFiles: (params?: BrowseFilesParams) => void;
|
|
27
|
+
getDropZoneProps: (additionalProps?: Record<string, any>) => Record<string, any>;
|
|
28
|
+
getLabelProps: (additionalProps?: Record<string, any>) => Record<string, any>;
|
|
29
|
+
validateFiles: (files: Array<{
|
|
30
|
+
type: string;
|
|
31
|
+
size: number;
|
|
32
|
+
}>) => FileError[];
|
|
33
|
+
}
|
|
34
|
+
export interface BrowserFilePickerConfig {
|
|
35
|
+
accept: string[];
|
|
36
|
+
multiple: boolean;
|
|
37
|
+
maxSize: string;
|
|
38
|
+
multipleMaxSize: string;
|
|
39
|
+
multipleMaxCount: number | null;
|
|
40
|
+
convertToBase64: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface BrowserFilePickerProps {
|
|
43
|
+
children: (params: BrowserFilePickerRenderProps) => React.ReactNode;
|
|
44
|
+
id?: string;
|
|
45
|
+
accept?: string[];
|
|
46
|
+
multiple?: boolean;
|
|
47
|
+
maxSize?: string;
|
|
48
|
+
multipleMaxSize?: string;
|
|
49
|
+
multipleMaxCount?: number | null;
|
|
50
|
+
convertToBase64?: boolean;
|
|
51
|
+
onSuccess?: (files: SelectedFile[]) => void;
|
|
52
|
+
onError?: (errors: FileError[], files: SelectedFile[]) => void;
|
|
53
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function generateId() {
|
|
2
|
+
return "_" + Math.random().toString(36).substr(2, 9);
|
|
3
|
+
}
|
|
4
|
+
function readFileContent(file) {
|
|
5
|
+
return new Promise((resolve, reject)=>{
|
|
6
|
+
const reader = new FileReader();
|
|
7
|
+
reader.onload = function(e) {
|
|
8
|
+
if (e.target) resolve(e.target.result);
|
|
9
|
+
else reject("Unable to read file contents!");
|
|
10
|
+
};
|
|
11
|
+
reader.readAsDataURL(file);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export { generateId, readFileContent };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/browserFilePicker/utils.js","sources":["../../../src/presentation/browserFilePicker/utils.ts"],"sourcesContent":["export function generateId(): string {\n return \"_\" + Math.random().toString(36).substr(2, 9);\n}\n\nexport function readFileContent(file: File): Promise<string> {\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = function (e) {\n if (e.target) {\n resolve(e.target.result as string);\n } else {\n reject(\"Unable to read file contents!\");\n }\n };\n reader.readAsDataURL(file);\n });\n}\n"],"names":["generateId","Math","readFileContent","file","Promise","resolve","reject","reader","FileReader","e"],"mappings":"AAAO,SAASA;IACZ,OAAO,MAAMC,KAAK,MAAM,GAAG,QAAQ,CAAC,IAAI,MAAM,CAAC,GAAG;AACtD;AAEO,SAASC,gBAAgBC,IAAU;IACtC,OAAO,IAAIC,QAAgB,CAACC,SAASC;QACjC,MAAMC,SAAS,IAAIC;QACnBD,OAAO,MAAM,GAAG,SAAUE,CAAC;YACvB,IAAIA,EAAE,MAAM,EACRJ,QAAQI,EAAE,MAAM,CAAC,MAAM;iBAEvBH,OAAO;QAEf;QACAC,OAAO,aAAa,CAACJ;IACzB;AACJ"}
|