1 // This file is part of Moodle - http://moodle.org/
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.
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/>.
17 * JavaScript to handle dropzone.
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
25 import {getString} from 'core/str';
26 import Log from 'core/log';
27 import {prefetchString} from 'core/prefetch';
28 import Templates from 'core/templates';
33 * @class core/dropzone
35 const DropZone = class {
38 * The element to render the dropzone.
44 * The file types that are allowed to be uploaded.
50 * The function to call when a file is dropped.
51 * @type {CallableFunction}
56 * The label to display in the dropzone.
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.
68 constructor(dropZoneElement, fileTypes, callback) {
69 prefetchString('core', 'addfilesdrop');
70 this.dropZoneElement = dropZoneElement;
71 this.fileTypes = fileTypes;
72 this.callback = callback;
76 * Initialise the dropzone.
81 this.dropZoneElement.addEventListener('dragover', (e) => {
82 const dropZone = this.getDropZoneFromEvent(e);
87 dropZone.classList.add('dragover');
89 this.dropZoneElement.addEventListener('dragleave', (e) => {
90 const dropZone = this.getDropZoneFromEvent(e);
95 dropZone.classList.remove('dragover');
97 this.dropZoneElement.addEventListener('drop', (e) => {
98 const dropZone = this.getDropZoneFromEvent(e);
103 dropZone.classList.remove('dragover');
104 this.callback(e.dataTransfer.files);
106 this.dropZoneElement.addEventListener('click', (e) => {
107 const dropZoneContainer = this.getDropZoneContainerFromEvent(e);
108 if (!dropZoneContainer) {
111 this.getFileElementFromEvent(e).click();
113 this.dropZoneElement.addEventListener('click', (e) => {
114 const dropZoneLabel = e.target.closest('.dropzone-sr-only-focusable');
115 if (!dropZoneLabel) {
118 this.getFileElementFromEvent(e).click();
120 this.dropZoneElement.addEventListener('change', (e) => {
121 const fileInput = this.getFileElementFromEvent(e);
124 this.callback(fileInput.files);
127 this.renderDropZone(this.dropZoneElement, this.fileTypes);
128 Log.info('Dropzone has been initialized!');
135 * @param {Event} e The event.
136 * @returns {HTMLElement|bool}
138 getDropZoneFromEvent(e) {
139 return e.target.closest('.dropzone');
143 * Get the dropzone container.
145 * @param {Event} e The event.
146 * @returns {HTMLElement|bool}
148 getDropZoneContainerFromEvent(e) {
149 return e.target.closest('.dropzone-container');
153 * Get the file element.
155 * @param {Event} e The event.
156 * @returns {HTMLElement|bool}
158 getFileElementFromEvent(e) {
159 return e.target.closest('.dropzone-container').querySelector('.drop-zone-fileinput');
163 * Set the label to display in the dropzone.
165 * @param {String} label The label to display in the dropzone.
168 this.dropZoneLabel = label;
172 * Get the label to display in the dropzone.
174 * @return {String} The label to display in the dropzone.
177 return this.dropZoneLabel;
181 * Render the dropzone.
183 * @param {Element} dropZoneElement The element to render the dropzone.
184 * @param {String} fileTypes The file types that are allowed to be uploaded.
187 async renderDropZone(dropZoneElement, fileTypes) {
188 if (!this.getLabel()) {
189 // Use the default one.
190 this.setLabel(await getString('addfilesdrop', 'core'));
192 const dropZoneLabel = this.getLabel();
193 dropZoneElement.innerHTML = await Templates.render('core/dropzone', {
194 label: dropZoneLabel,
195 filetypes: fileTypes,
200 export default DropZone;