@jaak.ai/file-uploader
Version:
Stencil Component Starter
289 lines (288 loc) • 11.8 kB
JavaScript
import { h, Host, Fragment, } from "@stencil/core";
import { checkFileSize, checkFileType } from "../../utils/utils";
import { decodeTiff, encodePng } from "image-in-browser";
export class FileUploader {
constructor() {
this.config = {
size: 5000,
accept: "",
description: "",
buttonText: "",
placeholder: "",
mode: "default",
};
this._preview = undefined;
this._base64 = undefined;
this._type = undefined;
this._selectedFile = {
base64: "",
filename: "",
size: 0,
sizeDescription: "",
};
this.documentPreview = false;
this.inputFileRef = undefined;
}
async selectFileHandler() {
const input = this._elementHost.shadowRoot.querySelector("input.fu-file-input");
input.click();
}
async onRemovedSelected() {
this._preview = "";
this.result.emit(null);
}
onChangeHandler(files) {
if (files.length === 1) {
const file = files[0];
if (this.config.size && !checkFileSize(file, this.config.size)) {
this.componentError.emit({ label: `Maximum file size exceeded. Max file size is: ${this.config.size} Bytes` });
return false;
}
else if (!checkFileType(file, this.config.accept)) {
this.componentError.emit({ label: "File type is not allowed" });
return false;
}
this.uploadFile(file);
}
else {
this.componentError.emit({
label: files.length === 0 ? "No file uploaded" : "You can only upload one file at the time",
});
return false;
}
}
uploadFile(file) {
const reader = new FileReader();
reader.onload = async () => {
let media = false;
if (checkFileType(file, "image/*")) {
this._type = "image";
media = true;
}
else if (checkFileType(file, "video/*")) {
this._type = "video";
media = true;
}
this._base64 = reader.result;
this._preview = reader.result;
if (this._type === "image" && ["image/tiff", "image/tif"].includes(file.type)) {
return this.tiffPreview(file);
}
this._selectedFile = {
base64: this._base64,
filename: file.name,
size: file.size,
sizeDescription: this.formatBytes(file.size),
type: file.type,
};
this.result.emit(this._selectedFile);
this.inputFileRef.value = null;
this.documentPreview = !media;
};
reader.onerror = (err) => {
this.componentError.emit({ label: "On load file error", details: err });
};
reader.readAsDataURL(file);
}
tiffPreview(file) {
const reader = new FileReader();
reader.onload = async () => {
const image = decodeTiff({
data: reader.result,
});
const imageArray = encodePng({
image,
});
this._preview = await this.blobToBase64(new Blob([imageArray], { type: "image/png" }));
this._selectedFile = {
base64: this._base64,
pngBase64: this._preview,
filename: file.name,
size: file.size,
sizeDescription: this.formatBytes(file.size),
type: file.type,
};
this.result.emit(this._selectedFile);
this.documentPreview = false;
this.inputFileRef.value = null;
};
reader.onerror = (err) => {
this.componentError.emit({ label: "On load file error", details: err });
};
reader.readAsArrayBuffer(file);
}
blobToBase64(blob) {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
}
formatBytes(bytes, decimals = 2) {
if (!+bytes) {
return "0 Bytes";
}
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}
dropFile(e) {
e.preventDefault();
if (e.dataTransfer.files.length) {
this.uploadFile(e.dataTransfer.files[0]);
}
}
defaultTemplate() {
return h(Fragment, null, h("div", { part: "drop-area", class: "fu-file-container " + (this._preview ? "selected-file" : ""), onDrop: e => this.dropFile(e) }, h("input", { class: "fu-file-input", type: "file", ref: el => (this.inputFileRef = el), onChange: ($event) => this.onChangeHandler($event.target.files), accept: this.config.accept }), h("span", { class: "fu-file-placeholder" }, this.config.placeholder), h("slot", { name: "placeholder" }), this._preview && h(Fragment, null, this._type === "image" &&
h("img", { src: this._preview, onLoad: () => this.documentPreview = false, onError: () => this.documentPreview = true, alt: "image preview" }), this._type === "video" && h("video", { src: this._preview, controls: true }), this.documentPreview && h("div", { class: "fu-document-preview" }, this._selectedFile.filename, " ", h("br", null), " ", h("small", null, this._selectedFile.sizeDescription)), h("button", { onClick: () => this.onRemovedSelected(), class: "fu-file-icon-button" }, h("div", { class: "icon-close-button" })))), this.config.description && h("p", { class: "fu-file-description" }, this.config.description), this.config.buttonText &&
h("button", { class: "fu-file-button", onClick: () => this.selectFileHandler() }, this.config.buttonText));
}
buttonTemplate() {
return h(Fragment, null, h("input", { class: "fu-file-input", type: "file", onChange: ($event) => this.onChangeHandler($event.target.files), accept: this.config.accept }), h("button", { class: "fu-file-button", onClick: () => this.selectFileHandler() }, this.config.buttonText));
}
hiddenTemplate() {
return h(Fragment, null, h("input", { class: "fu-file-input", type: "file", onChange: ($event) => this.onChangeHandler($event.target.files), accept: this.config.accept }));
}
render() {
return h(Host, { key: 'd71833baee55a939c90a5dde36facd956f845a20' }, this.config.mode === "default" && this.defaultTemplate(), this.config.mode === "button" && this.buttonTemplate(), this.config.mode === "hidden" && this.hiddenTemplate());
}
static get is() { return "file-uploader"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["file-uploader.scss"]
};
}
static get styleUrls() {
return {
"$": ["file-uploader.css"]
};
}
static get properties() {
return {
"config": {
"type": "unknown",
"mutable": false,
"complexType": {
"original": "FileUploaderConfig",
"resolved": "FileUploaderConfig",
"references": {
"FileUploaderConfig": {
"location": "import",
"path": "../../interfaces/file-uploader-config",
"id": "src/interfaces/file-uploader-config.ts::FileUploaderConfig"
}
}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": ""
},
"defaultValue": "{\n size: 5000,\n accept: \"\",\n description: \"\",\n buttonText: \"\",\n placeholder: \"\",\n mode: \"default\",\n }"
}
};
}
static get states() {
return {
"_preview": {},
"_base64": {},
"_type": {},
"_selectedFile": {},
"documentPreview": {},
"inputFileRef": {}
};
}
static get events() {
return [{
"method": "result",
"name": "result",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "On file change event emitter"
},
"complexType": {
"original": "FileUploaderResult | null",
"resolved": "FileUploaderResult",
"references": {
"FileUploaderResult": {
"location": "import",
"path": "../../interfaces/file-uploader-result",
"id": "src/interfaces/file-uploader-result.ts::FileUploaderResult"
}
}
}
}, {
"method": "componentError",
"name": "componentError",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": "On error event emitter"
},
"complexType": {
"original": "WebComponentError",
"resolved": "WebComponentError",
"references": {
"WebComponentError": {
"location": "import",
"path": "../../interfaces/web-component-error",
"id": "src/interfaces/web-component-error.ts::WebComponentError"
}
}
}
}];
}
static get methods() {
return {
"selectFileHandler": {
"complexType": {
"signature": "() => Promise<void>",
"parameters": [],
"references": {
"Promise": {
"location": "global",
"id": "global::Promise"
},
"HTMLInputElement": {
"location": "global",
"id": "global::HTMLInputElement"
}
},
"return": "Promise<void>"
},
"docs": {
"text": "",
"tags": []
}
},
"onRemovedSelected": {
"complexType": {
"signature": "() => Promise<void>",
"parameters": [],
"references": {
"Promise": {
"location": "global",
"id": "global::Promise"
}
},
"return": "Promise<void>"
},
"docs": {
"text": "",
"tags": []
}
}
};
}
static get elementRef() { return "_elementHost"; }
}
//# sourceMappingURL=file-uploader.js.map