-
Notifications
You must be signed in to change notification settings - Fork 9.3k
/
drag-and-drop.js
92 lines (80 loc) · 2.29 KB
/
drag-and-drop.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/* global logger */
/**
* Manages drag and drop file input for the page.
*/
export class DragAndDrop {
/**
* @param {function(string): void} fileHandlerCallback Invoked when the user chooses a new file.
*/
constructor(fileHandlerCallback) {
const dropZone = document.querySelector('.drop_zone');
if (!dropZone) {
throw new Error('Drag and drop `.drop_zone` element not found in page');
}
this._dropZone = dropZone;
this._fileHandlerCallback = fileHandlerCallback;
this._dragging = false;
this._addListeners();
}
/**
* Reads a file and returns its content as a string.
* @param {File} file
* @return {Promise<string>}
*/
readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(e) {
const result = /** @type {?string} */ (e.target?.result);
if (!result) {
reject(new Error('Could not read file'));
return;
}
resolve(result);
};
reader.onerror = reject;
reader.readAsText(file);
});
}
_addListeners() {
// The mouseleave event is more reliable than dragleave when the user drops
// the file outside the window.
document.addEventListener('mouseleave', _ => {
if (!this._dragging) {
return;
}
this._resetDraggingUI();
});
document.addEventListener('dragover', e => {
e.stopPropagation();
e.preventDefault();
if (e.dataTransfer) {
e.dataTransfer.dropEffect = 'copy'; // Explicitly show as copy action.
}
});
document.addEventListener('dragenter', _ => {
this._dropZone.classList.add('dropping');
this._dragging = true;
});
document.addEventListener('drop', e => {
e.stopPropagation();
e.preventDefault();
this._resetDraggingUI();
// Note, this ignores multiple files in the drop, only taking the first.
if (e.dataTransfer) {
this.readFile(e.dataTransfer.files[0]).then((str) => {
this._fileHandlerCallback(str);
}).catch(e => logger.error(e));
}
});
}
_resetDraggingUI() {
this._dropZone.classList.remove('dropping');
this._dragging = false;
}
}