/* eslint-disable ts/no-this-alias */ import type { VNode } from 'vue'; import type { _ButtonArgsBase, ButtonLayout, ButtonSize } from './button-layout'; import { Prop, toNative } from 'vue-facing-decorator'; import TsxComponent, { Component } from '../../app/vuetsx'; import { isNullOrEmpty } from '../../common/utils/is-null-or-empty'; import Button from './button'; const enum UploadButtonTarget { Local = 0, Server = 1, } export interface UploadButtonClienstideChangedArgs { fileName: string; srcBase64: string; } export interface UploadButtonFileObtainedArgs { file: File; } interface UploadButtonArgs extends _ButtonArgsBase { target?: UploadButtonTarget; asArrayBuffer?: boolean; asString?: boolean; asFile?: boolean; changedClientside?: (args: UploadButtonClienstideChangedArgs) => void; fileObtained?: (args: UploadButtonFileObtainedArgs) => void; onlyImages?: boolean; onlyExcel?: boolean; onlyVideo?: boolean; accept?: string; } @Component class UploadButtonComponent extends TsxComponent implements UploadButtonArgs { @Prop() cssClass!: string; @Prop() layout!: ButtonLayout; @Prop() text!: string | VNode; @Prop() tooltip!: string; @Prop() size!: ButtonSize; @Prop() round!: boolean; @Prop() outlined!: boolean; @Prop() dismissModal!: boolean; @Prop() icon!: string; @Prop() iconOnRight!: boolean; @Prop() disabled!: boolean; @Prop() fullWidth!: boolean; @Prop() onlyImages?: boolean; @Prop() onlyExcel?: boolean; @Prop() onlyVideo?: boolean; @Prop() asFile?: boolean; @Prop() asArrayBuffer?: boolean; @Prop() asString?: boolean; @Prop() pulsate!: boolean; @Prop() accept?: string; @Prop() changedClientside: (args: UploadButtonClienstideChangedArgs) => void; @Prop() fileObtained: (args: UploadButtonFileObtainedArgs) => void; getAccept(): string { if (!isNullOrEmpty(this.accept)) { return this.accept; } else if (this.onlyImages == true) { return 'image/*'; } else if (this.onlyExcel == true) { return '.xlsx, .xls, .ods'; } else if (this.onlyVideo == true) { return 'video/mp4,video/x-m4v,video/*'; } return null; } resetFileInput() { const input = this.$refs.clientImageLoader as HTMLInputElement; input.value = ''; if (!/safari/i.test(navigator.userAgent)) { input.type = ''; input.type = 'file'; } } handleClientsideChange(file: File) { const reader = new FileReader(); const mySelf = this; reader.onload = function (event) { mySelf.resetFileInput(); if (mySelf.changedClientside != null) { mySelf.changedClientside({ fileName: file.name, srcBase64: event.target.result as any, }); } }; if (this.fileObtained != null) { this.fileObtained({ file, }); } if (this.asFile) { // asFile callers return before any reader.readAs* call, so reader.onload — // the only place that clears the hidden input — never fires. Reset here too, // otherwise re-selecting the same file emits no `change` event and the // selection is silently dropped. this.resetFileInput(); return; } if (this.asArrayBuffer == true) { reader.readAsArrayBuffer(file); } else if (this.asString == true) { reader.readAsText(file); } else { reader.readAsDataURL(file); } } handleClientButtonClicked() { (this.$refs.clientImageLoader as HTMLButtonElement).click(); } onFileChanged(e) { this.handleClientsideChange(e.target.files[0]); } handleDrop(e) { const file = e.dataTransfer.files[0]; if (file != null) { this.handleClientsideChange(file); } } handleDummyEvent(e: Event) { e.preventDefault(); e.stopPropagation(); } bindDrop() { this.$nextTick(() => { const targetEl = (this.$refs.uploadButton as any).$el as HTMLElement; if ((targetEl as any)._eventsBound == null) { (targetEl as any)._eventsBound = true; [ 'drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop', ].forEach((evType) => { targetEl.addEventListener(evType, this.handleDummyEvent); }); targetEl.addEventListener('drop', (e) => { this.handleDrop(e); }); } }); } render(h) { return this.renderClientsideUpload(h); } renderClientsideUpload(h) { this.bindDrop(); return (