]> BookStack Code Mirror - bookstack/blob - resources/js/components/dropzone.js
Started custom dropzone implementation
[bookstack] / resources / js / components / dropzone.js
1 import {Component} from './component';
2 import {Clipboard} from '../services/clipboard';
3
4 export class Dropzone extends Component {
5
6     setup() {
7         this.container = this.$el;
8         this.url = this.$opts.url;
9         this.successMessage = this.$opts.successMessage;
10         this.removeMessage = this.$opts.removeMessage;
11         this.uploadLimit = Number(this.$opts.uploadLimit); // TODO - Use
12         this.uploadLimitMessage = this.$opts.uploadLimitMessage; // TODO - Use
13         this.timeoutMessage = this.$opts.timeoutMessage; // TODO - Use
14         // window.uploadTimeout // TODO - Use
15         // TODO - Click-to-upload buttons/areas
16         // TODO - Drop zone highlighting of existing element
17         //   (Add overlay via additional temp element).
18
19         this.setupListeners();
20     }
21
22     setupListeners() {
23         this.container.addEventListener('dragenter', event => {
24             this.container.style.border = '1px dotted tomato';
25             event.preventDefault();
26         });
27
28         this.container.addEventListener('dragover', event => {
29             event.preventDefault();
30         });
31
32         const reset = () => {
33             this.container.style.border = null;
34         };
35
36         this.container.addEventListener('dragend', event => {
37             reset();
38         });
39
40         this.container.addEventListener('dragleave', event => {
41             reset();
42         });
43
44         this.container.addEventListener('drop', event => {
45             event.preventDefault();
46             const clipboard = new Clipboard(event.dataTransfer);
47             const files = clipboard.getFiles();
48             for (const file of files) {
49                 this.createUploadFromFile(file);
50             }
51         });
52     }
53
54     /**
55      * @param {File} file
56      * @return {Upload}
57      */
58     createUploadFromFile(file) {
59         const {dom, status} = this.createDomForFile(file);
60         this.container.append(dom);
61
62         const formData = new FormData();
63         formData.append('file', file, file.name);
64
65         // TODO - Change to XMLHTTPRequest so we can track progress.
66         const uploadPromise = window.$http.post(this.url, formData);
67
68         const upload = {
69             file,
70             dom,
71             markError(message) {
72                 status.setAttribute('data-status', 'error');
73                 status.textContent = message;
74             },
75             markSuccess(message) {
76                 status.setAttribute('data-status', 'success');
77                 status.textContent = message;
78             },
79         };
80
81         uploadPromise.then(returnData => {
82             upload.markSuccess(returnData.statusText);
83         }).catch(err => {
84             upload.markError(err?.data?.message || err.message);
85         });
86
87         return upload;
88     }
89
90     /**
91      * @param {File} file
92      * @return {{image: Element, dom: Element, progress: Element, label: Element, status: Element}}
93      */
94     createDomForFile(file) {
95         const dom = document.createElement('div');
96         const label = document.createElement('div');
97         const status = document.createElement('div');
98         const progress = document.createElement('div');
99         const image = document.createElement('img');
100
101         dom.classList.add('dropzone-file-item');
102         status.classList.add('dropzone-file-item-status');
103         progress.classList.add('dropzone-file-item-progress');
104
105         image.src = ''; // TODO - file icon
106         label.innerText = file.name;
107
108         if (file.type.startsWith('image/')) {
109             image.src = URL.createObjectURL(file);
110         }
111
112         dom.append(image, label, progress, status);
113         return {
114             dom, label, image, progress, status,
115         };
116     }
117
118 }
119
120 /**
121  * @typedef Upload
122  * @property {File} file
123  * @property {Element} dom
124  * @property {function(String)} markError
125  * @property {function(String)} markSuccess
126  */