Merge branch 'MDL-81713-main' of https://github.com/junpataleta/moodle
[moodle.git] / lib / amd / src / dropzone.js
blob9bc8d99d1d6343e12456efc69ec1baa5908e7df3
1 // This file is part of Moodle - http://moodle.org/
2 //
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16 /**
17  * JavaScript to handle dropzone.
18  *
19  * @module     core/dropzone
20  * @copyright  2024 Huong Nguyen <huongnv13@gmail.com>
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  * @since      4.4
23  */
25 import {getString} from 'core/str';
26 import Log from 'core/log';
27 import {prefetchString} from 'core/prefetch';
28 import Templates from 'core/templates';
30 /**
31  * A dropzone.
32  *
33  * @class core/dropzone
34  */
35 const DropZone = class {
37     /**
38      * The element to render the dropzone.
39      * @type {Element}
40      */
41     dropZoneElement;
43     /**
44      * The file types that are allowed to be uploaded.
45      * @type {String}
46      */
47     fileTypes;
49     /**
50      * The function to call when a file is dropped.
51      * @type {CallableFunction}
52      */
53     callback;
55     /**
56      * The label to display in the dropzone.
57      * @type {string}
58      */
59     dropZoneLabel = '';
61     /**
62      * Constructor.
63      *
64      * @param {Element} dropZoneElement The element to render the dropzone.
65      * @param {String} fileTypes The file types that are allowed to be uploaded. Example: image/*
66      * @param {CallableFunction} callback The function to call when a file is dropped.
67      */
68     constructor(dropZoneElement, fileTypes, callback) {
69         prefetchString('core', 'addfilesdrop');
70         this.dropZoneElement = dropZoneElement;
71         this.fileTypes = fileTypes;
72         this.callback = callback;
73     }
75     /**
76      * Initialise the dropzone.
77      *
78      * @returns {DropZone}
79      */
80     init() {
81         this.dropZoneElement.addEventListener('dragover', (e) => {
82             const dropZone = this.getDropZoneFromEvent(e);
83             if (!dropZone) {
84                 return;
85             }
86             e.preventDefault();
87             dropZone.classList.add('dragover');
88         });
89         this.dropZoneElement.addEventListener('dragleave', (e) => {
90             const dropZone = this.getDropZoneFromEvent(e);
91             if (!dropZone) {
92                 return;
93             }
94             e.preventDefault();
95             dropZone.classList.remove('dragover');
96         });
97         this.dropZoneElement.addEventListener('drop', (e) => {
98             const dropZone = this.getDropZoneFromEvent(e);
99             if (!dropZone) {
100                 return;
101             }
102             e.preventDefault();
103             dropZone.classList.remove('dragover');
104             this.callback(e.dataTransfer.files);
105         });
106         this.dropZoneElement.addEventListener('click', (e) => {
107             const dropZoneContainer = this.getDropZoneContainerFromEvent(e);
108             if (!dropZoneContainer) {
109                 return;
110             }
111             this.getFileElementFromEvent(e).click();
112         });
113         this.dropZoneElement.addEventListener('click', (e) => {
114             const dropZoneLabel = e.target.closest('.dropzone-sr-only-focusable');
115             if (!dropZoneLabel) {
116                 return;
117             }
118             this.getFileElementFromEvent(e).click();
119         });
120         this.dropZoneElement.addEventListener('change', (e) => {
121             const fileInput = this.getFileElementFromEvent(e);
122             if (fileInput) {
123                 e.preventDefault();
124                 this.callback(fileInput.files);
125             }
126         });
127         this.renderDropZone(this.dropZoneElement, this.fileTypes);
128         Log.info('Dropzone has been initialized!');
129         return this;
130     }
132     /**
133      * Get the dropzone.
134      *
135      * @param {Event} e The event.
136      * @returns {HTMLElement|bool}
137      */
138     getDropZoneFromEvent(e) {
139         return e.target.closest('.dropzone');
140     }
142     /**
143      * Get the dropzone container.
144      *
145      * @param {Event} e The event.
146      * @returns {HTMLElement|bool}
147      */
148     getDropZoneContainerFromEvent(e) {
149         return e.target.closest('.dropzone-container');
150     }
152     /**
153      * Get the file element.
154      *
155      * @param {Event} e The event.
156      * @returns {HTMLElement|bool}
157      */
158     getFileElementFromEvent(e) {
159         return e.target.closest('.dropzone-container').querySelector('.drop-zone-fileinput');
160     }
162     /**
163      * Set the label to display in the dropzone.
164      *
165      * @param {String} label The label to display in the dropzone.
166      */
167     setLabel(label) {
168         this.dropZoneLabel = label;
169     }
171     /**
172      * Get the label to display in the dropzone.
173      *
174      * @return {String} The label to display in the dropzone.
175      */
176     getLabel() {
177         return this.dropZoneLabel;
178     }
180     /**
181      * Render the dropzone.
182      *
183      * @param {Element} dropZoneElement The element to render the dropzone.
184      * @param {String} fileTypes The file types that are allowed to be uploaded.
185      * @returns {Promise}
186      */
187     async renderDropZone(dropZoneElement, fileTypes) {
188         if (!this.getLabel()) {
189             // Use the default one.
190             this.setLabel(await getString('addfilesdrop', 'core'));
191         }
192         const dropZoneLabel = this.getLabel();
193         dropZoneElement.innerHTML = await Templates.render('core/dropzone', {
194             label: dropZoneLabel,
195             filetypes: fileTypes,
196         });
197     }
200 export default DropZone;