class UploadBoxWidget { /** @param {string} formId @param {boolean} handleSubmit */ constructor(formId = 'upload-box', handleSubmit = true) { this.handleSubmit = handleSubmit; this.form = document.getElementById(formId); if (this.form === null) { return; } this.uploadBox = this.form.querySelector('[data-box-input-container]'); this.input = this.form.querySelector('input[type="file"]'); this.uploadFileButton = this.form.querySelector('[data-action="upload-files"]'); this.removeFileButton = this.form.querySelector('[data-action="remove-files"]'); this.selectedFilesContainer = this.form.querySelector('[data-selected-files-container]'); this.messages = this.form.querySelector('[data-upload-box-messages] span') this.progress = this.form.querySelector('progress'); this.droppedFiles = false; this.initialize(); } initialize() { document.addEventListener("DOMContentLoaded", () => { this.form.addEventListener('click', this.onClickDiv.bind(this)); this.form.addEventListener('submit', this.onSubmit.bind(this)); this.input.addEventListener('change', this.onFileInputChange.bind(this)); ['drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave', 'drop'] .forEach(strEvent => this.form.addEventListener(strEvent, this.preventEvent.bind(this))); ['dragover', 'dragenter'] .forEach(strEvent => this.form.addEventListener(strEvent, this.onDragEvent.bind(this))); ['dragleave', 'dragend', 'drop'] .forEach(strEvent => this.form.addEventListener(strEvent, this.onDragEndEvent.bind(this))); }); } /** * @param {Event} event */ preventEvent(event) { event.preventDefault(); event.stopPropagation(); } /** * @param {SubmitEvent} event * @return {Promise|boolean} */ onSubmit(event) { if (!this.handleSubmit) { return; } event.preventDefault(); if (this.form.classList.contains('is-uploading')) { return false; } this.form.classList.add('is-uploading'); this.form.classList.remove('is-error'); this.form.classList.remove('is-success'); this.messages.textContent = ''; this.removeFileButton.classList.add('d-none'); if (this.uploadFileButton) { this.uploadFileButton.disabled = true; } let ajaxData = new FormData(this.form); if (this.droppedFiles) { Array.from(this.droppedFiles).forEach(file => { ajaxData.append('files[]', file); }) } document.dispatchEvent(new CustomEvent('upload-started')); return axios( { method: "post", url: this.form.getAttribute('action'), data: ajaxData, headers: { "Content-Type": "multipart/form-data", 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content, }, onUploadProgress: (event) => { if (event.bytes) { let percentComplete = 100 * (event.loaded / event.total); document.dispatchEvent(new CustomEvent('upload-progress', {detail: {progress: percentComplete}})); this.progress.value = percentComplete; this.progress.textContent = percentComplete + '%'; } } }) .then(response => response.json) .then(data => { this.form.classList.add('is-success'); document.dispatchEvent(new CustomEvent('upload-success')); }) .catch(ex => { this.form.classList.add('is-error'); document.dispatchEvent(new CustomEvent('upload-error')); }) .finally(() => { this.hideFiles(); this.form.classList.remove('is-uploading'); document.dispatchEvent(new CustomEvent('upload-complete')); }) } /** * @param {Event} event * @return {boolean} */ onFileInputChange(event) { let input = event.target; let maxSize = false; Array.from(input.files).forEach( file => { let filesize = ((file.size / 1024) / 1024).toFixed(4); // MB if (filesize > 10) { maxSize = true; return false; } } ) if (maxSize) { input.value = null; return false; } this.droppedFiles = input.files; this.showFiles(); } /** * @param {DragEvent} event */ onDragEvent(event) { this.uploadBox.classList.add('dragover'); if (event.dataTransfer) { event.dataTransfer.dropEffect = 'copy'; } } /** * @param {DragEvent} event */ onDragEndEvent(event) { this.uploadBox.classList.remove('dragover'); if (event.type === 'drop') { this.onDrop(event); } } /** * @param {DragEvent} event */ onDrop(event) { this.droppedFiles = false; let files = Array.from(event.dataTransfer.files); this.droppedFiles = files.filter(file => { let filesize = ((file.size / 1024) / 1024).toFixed(4); // MB return filesize < 10 && (file.type.match(/image.*/) || file.type.match(/pdf.*/)); }) this.showFiles(); if (this.form.dataset.ajax !== 'true') { this.input.files = new FileListItems(this.droppedFiles); } } /** * @param {MouseEvent} event */ onClickDiv(event) { if (event.target.closest('[data-action="upload-files"]')) { event.stopImmediatePropagation(); return false; } if (event.target.closest('[data-action="remove-files"]')) { this.hideFiles(); event.target.classList.add('d-none'); document.dispatchEvent(new CustomEvent('upload-files-removed')); event.stopImmediatePropagation(); return false; } if (event.target.closest('[data-box-input-container]')) { event.stopImmediatePropagation(); this.input.click(); return false; } } hideFiles() { this.selectedFilesContainer.textContent = ''; this.selectedFilesContainer.classList.add('d-none'); this.droppedFiles = false; if (this.uploadFileButton) { this.uploadFileButton.disabled = true; } } /** * @param File[] files * @return {File[]} */ showFiles() { let files = Array.from(this.droppedFiles); if (files[0] === undefined) { return; } this.form.classList.remove('is-uploading', 'is-success', 'is-error'); this.selectedFilesContainer.classList.remove('d-none'); if (files.length > 1) { this.selectedFilesContainer.textContent = (this.input.dataset.multipleCaption || '').replace('{count}', files.length); } else { this.selectedFilesContainer.textContent = files[0].name } this.removeFileButton.classList.remove('d-none'); document.dispatchEvent(new CustomEvent('upload-files-added', {detail: {files: files}})); files.forEach(file => { document.dispatchEvent(new CustomEvent('upload-file-added', {detail: {file: file}})); }) if (this.uploadFileButton) { this.uploadFileButton.disabled = false; } return files; }; /** * @params {File[]} files Array of files to add to the FileList * @return {FileList} */ FileListItems(files) { let b = new ClipboardEvent("").clipboardData || new DataTransfer() for (let i = 0, len = files.length; i < len; i++) b.items.add(files[i]) return b.files } }