UNPKG

@jaak.ai/file-uploader

Version:
289 lines (288 loc) 11.8 kB
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