Skip to content

Commit 00eb65b

Browse files
committed
[web] support dragging and pasting data file urls
1 parent 7a5e3e6 commit 00eb65b

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

src/gui/gui-import-control.mjs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,20 @@ function DropControl(gui, el, cb) {
1818
.on('paste', onpaste);
1919
area.node().addEventListener('paste', onpaste);
2020
function ondrop(e) {
21+
var files = e.dataTransfer.files;
22+
var types = e.dataTransfer.types;
2123
block(e);
22-
cb(e.dataTransfer.files);
24+
if (files.length) {
25+
cb(files);
26+
} else if (types.includes('text/uri-list')) {
27+
cb(e.dataTransfer.getData('text/uri-list').split(','));
28+
} else if (types.includes('text/html')) {
29+
// drag-dropping a highlighted link may pull in a chunk of html
30+
var urls = e.dataTransfer.getData('text/html').match(/https?:[^"']+/);
31+
if (urls.length) {
32+
cb(urls);
33+
}
34+
}
2335
}
2436
function onpaste(e) {
2537
var types = Array.from(e.clipboardData.types || []).join(',');
@@ -36,15 +48,18 @@ function DropControl(gui, el, cb) {
3648
// Single files of all types are pasted as a string and an image/png
3749
// Multiple files are pasted as a string containing a list of file names
3850

39-
// import text from the clipboard (could be csv, json, etc)
51+
// import text from the clipboard (could be csv, json, a url, etc)
4052
// formatted text can be available as both text/plain and text/html (e.g.
4153
// a JSON data object copied from a GitHub issue).
4254
//
4355
if (types.includes('text/plain')) {
44-
// if (types == 'text/plain') {
4556
// text from clipboard (supported by Chrome, FF, Safari)
4657
// TODO: handle FF case of string containing multiple file names.
47-
files = [pastedTextToFile(e.clipboardData.getData('text/plain'))];
58+
var str = e.clipboardData.getData('text/plain');
59+
if (isUrl(str)) {
60+
return cb(str.split(','));
61+
}
62+
files = [pastedTextToFile(str)];
4863
} else {
4964
files = items.map(function(item) {
5065
return item.kind == 'file' && !item.type.includes('image') ?
@@ -78,6 +93,10 @@ function pastedTextToFile(str) {
7893
return new File([blob], name);
7994
}
8095

96+
function isUrl(str) {
97+
return /^https?:\/\//.test(str);
98+
}
99+
81100
// @el DOM element for select button
82101
// @cb function(<FileList>)
83102
function FileChooser(el, cb) {
@@ -121,7 +140,7 @@ export function ImportControl(gui, opts) {
121140

122141
var submitBtn = new SimpleButton('#import-options .submit-btn').on('click', importQueuedFiles);
123142
new SimpleButton('#import-options .cancel-btn').on('click', gui.clearMode);
124-
new DropControl(gui, 'body', receiveFilesWithOption);
143+
new DropControl(gui, 'body', receiveDroppedItems);
125144
new FileChooser('#import-options .add-btn', receiveFilesWithOption);
126145
new FileChooser('#add-file-btn', receiveFiles);
127146
new SimpleButton('#add-empty-btn').on('click', function() {
@@ -137,7 +156,7 @@ export function ImportControl(gui, opts) {
137156

138157
function turnOn() {
139158
if (manifestFiles.length > 0) {
140-
downloadFiles(manifestFiles, true);
159+
downloadFiles(manifestFiles);
141160
manifestFiles = [];
142161
} else if (model.isEmpty()) {
143162
showImportMenu();
@@ -238,6 +257,14 @@ export function ImportControl(gui, opts) {
238257
submitBtn.classed('disabled', queuedFiles.length === 0);
239258
}
240259

260+
function receiveDroppedItems(arr) {
261+
if (utils.isString(arr[0])) { // assume array of URLs
262+
downloadFiles(arr);
263+
} else { // assume array of Files
264+
receiveFilesWithOption(arr);
265+
}
266+
}
267+
241268
function receiveFilesWithOption(files) {
242269
var quickView = !El('.advanced-import-options').node().checked;
243270
receiveFiles(files, quickView);
@@ -393,9 +420,8 @@ export function ImportControl(gui, opts) {
393420

394421
function prepFilesForDownload(names) {
395422
var items = names.map(function(name) {
396-
var isUrl = /:\/\//.test(name);
397423
var item = {name: name};
398-
if (isUrl) {
424+
if (isUrl(name)) {
399425
item.url = name;
400426
item.basename = GUI.getUrlFilename(name);
401427

src/gui/gui-proxy.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export function ImportFileProxy(gui) {
8080
var lyr;
8181
if (memo) return memo; // already found a match
8282
// try to match import filename of this dataset
83-
if (d.info.input_files[0] == src) return d;
83+
if (d.info?.input_files?.[0] == src) return d;
8484
// try to match name of a layer in this dataset
8585
lyr = utils.find(d.layers, function(lyr) {return lyr.name == src;});
8686
return lyr ? internal.isolateLayer(lyr, d) : null;

0 commit comments

Comments
 (0)