1 import {Component} from './component';
2 import {Clipboard} from '../services/clipboard';
4 elem, getLoading, removeLoading,
5 } from '../services/dom';
7 export class Dropzone extends Component {
10 this.container = this.$el;
11 this.statusArea = this.$refs.statusArea;
13 this.url = this.$opts.url;
14 this.successMessage = this.$opts.successMessage;
15 this.removeMessage = this.$opts.removeMessage;
16 this.uploadLimit = Number(this.$opts.uploadLimit); // TODO - Use
17 this.uploadLimitMessage = this.$opts.uploadLimitMessage; // TODO - Use
18 this.timeoutMessage = this.$opts.timeoutMessage; // TODO - Use
19 this.zoneText = this.$opts.zoneText;
20 // window.uploadTimeout // TODO - Use
21 // TODO - Click-to-upload buttons/areas
22 // TODO - Drop zone highlighting of existing element
23 // (Add overlay via additional temp element).
25 this.setupListeners();
31 this.container.addEventListener('dragenter', event => {
32 event.preventDefault();
40 this.container.addEventListener('dragover', event => {
41 event.preventDefault();
49 this.container.addEventListener('dragend', event => {
53 this.container.addEventListener('dragleave', event => {
60 this.container.addEventListener('drop', event => {
61 event.preventDefault();
63 const clipboard = new Clipboard(event.dataTransfer);
64 const files = clipboard.getFiles();
65 for (const file of files) {
66 this.createUploadFromFile(file);
72 const overlay = this.container.querySelector('.dropzone-overlay');
74 const zoneElem = elem('div', {class: 'dropzone-overlay'}, [this.zoneText]);
75 this.container.append(zoneElem);
80 const overlay = this.container.querySelector('.dropzone-overlay');
90 createUploadFromFile(file) {
91 const {dom, status, progress} = this.createDomForFile(file);
92 this.container.append(dom);
97 updateProgress(percentComplete) {
98 console.log(`progress: ${percentComplete}%`);
99 progress.textContent = `${percentComplete}%`;
100 progress.style.width = `${percentComplete}%`;
103 status.setAttribute('data-status', 'error');
104 status.textContent = message;
107 markSuccess(message) {
108 status.setAttribute('data-status', 'success');
109 status.textContent = message;
114 this.startXhrForUpload(upload);
120 * @param {Upload} upload
122 startXhrForUpload(upload) {
123 const formData = new FormData();
124 formData.append('file', upload.file, upload.file.name);
126 const req = window.$http.createXMLHttpRequest('POST', this.url, {
128 upload.markError('Upload failed'); // TODO - Update text
131 if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
132 upload.markSuccess('Finished upload!');
133 } else if (this.readyState === XMLHttpRequest.DONE && this.status >= 400) {
134 const content = this.responseText;
135 const data = content.startsWith('{') ? JSON.parse(content) : {message: content};
136 const message = data?.message || content;
137 upload.markError(message);
142 req.upload.addEventListener('progress', evt => {
143 const percent = Math.min(Math.ceil((evt.loaded / evt.total) * 100), 100);
144 upload.updateProgress(percent);
147 req.setRequestHeader('Accept', 'application/json');
153 * @return {{image: Element, dom: Element, progress: Element, label: Element, status: Element}}
155 createDomForFile(file) {
156 const image = elem('img', {src: ''});
157 const status = elem('div', {class: 'dropzone-file-item-status'}, []);
158 const progress = elem('div', {class: 'dropzone-file-item-progress'});
159 const imageWrap = elem('div', {class: 'dropzone-file-item-image-wrap'}, [image]);
161 const dom = elem('div', {class: 'dropzone-file-item'}, [
163 elem('div', {class: 'dropzone-file-item-text-wrap'}, [
164 elem('div', {class: 'dropzone-file-item-label'}, [file.name]),
171 if (file.type.startsWith('image/')) {
172 image.src = URL.createObjectURL(file);
176 dom, progress, status,
184 * @property {File} file
185 * @property {Element} dom
186 * @property {function(Number)} updateProgress
187 * @property {function(String)} markError
188 * @property {function(String)} markSuccess