You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2019/10/01 16:47:42 UTC
[airavata-django-portal] branch master updated: AIRAVATA-3081 Tus
uploads: switch to Drag & Drop UI
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git
The following commit(s) were added to refs/heads/master by this push:
new 5352078 AIRAVATA-3081 Tus uploads: switch to Drag & Drop UI
5352078 is described below
commit 5352078866be1da27d9b19597a9927714749a902
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Tue Oct 1 12:44:31 2019 -0400
AIRAVATA-3081 Tus uploads: switch to Drag & Drop UI
---
django_airavata/apps/workspace/package.json | 2 +-
.../experiment/input-editors/FileInputEditor.vue | 107 +++-------------
.../experiment/input-editors/InputFileSelector.vue | 138 +++++++++++++++++++++
.../input-editors/MultiFileInputEditor.vue | 114 ++++++-----------
.../components/experiment/input-editors/Uppy.vue | 44 +++++--
.../js/containers/UserStorageContainer.vue | 1 +
6 files changed, 224 insertions(+), 182 deletions(-)
diff --git a/django_airavata/apps/workspace/package.json b/django_airavata/apps/workspace/package.json
index aadd26f..c3e3a63 100644
--- a/django_airavata/apps/workspace/package.json
+++ b/django_airavata/apps/workspace/package.json
@@ -15,7 +15,7 @@
},
"dependencies": {
"@uppy/core": "^1.2.0",
- "@uppy/file-input": "^1.2.0",
+ "@uppy/drag-drop": "^1.3.0",
"@uppy/status-bar": "^1.2.0",
"@uppy/tus": "^1.3.0",
"@uppy/xhr-upload": "^1.2.0",
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
index fcc3c06..d3059ce 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
@@ -40,36 +40,13 @@
></i>
</b-link>
</div>
- <div v-if="isSelectingFile">
- <user-storage-file-selection-container
- @file-selected="fileSelected"
- @cancel="cancelFileSelection"
- :selected-data-product-uris="selectedDataProductURIs"
- />
- </div>
- <div
- class="d-flex align-items-baseline"
- v-if="!isSelectingFile && !isDataProductURI"
- >
- <b-button
- @click="isSelectingFile=true"
- class="input-file-option"
- >Select file from storage</b-button>
- <span class="text-muted mx-3">OR</span>
- <b-form-group
- :description="maxFileUploadSizeMessage"
- :state="fileUploadState"
- :invalid-feedback="fileUploadInvalidFeedback"
- class="input-file-option"
- >
- <uppy
- xhr-upload-endpoint="/api/upload"
- tus-upload-finish-endpoint="/api/tus-upload-finish"
- @upload-success="uploadSuccess"
- @upload-started="$emit('uploadstart')"
- />
- </b-form-group>
- </div>
+ <input-file-selector
+ v-if="!isDataProductURI"
+ :selectedDataProductURIs="selectedDataProductURIs"
+ @uploadstart="$emit('uploadstart')"
+ @uploadend="$emit('uploadend')"
+ @selected="fileSelected"
+ />
</div>
</template>
@@ -77,8 +54,7 @@
import { models, services, utils } from "django-airavata-api";
import { InputEditorMixin } from "django-airavata-workspace-plugin-api";
import { components } from "django-airavata-common-ui";
-import UserStorageFileSelectionContainer from "../../storage/UserStorageFileSelectionContainer";
-import Uppy from "./Uppy";
+import InputFileSelector from "./InputFileSelector";
export default {
name: "file-input-editor",
@@ -86,8 +62,7 @@ export default {
components: {
"data-product-viewer": components.DataProductViewer,
"delete-link": components.DeleteLink,
- UserStorageFileSelectionContainer,
- Uppy
+ InputFileSelector
},
computed: {
isDataProductURI() {
@@ -110,54 +85,11 @@ export default {
} else {
return [];
}
- },
- maxFileUploadSizeMB() {
- return this.settings
- ? this.settings.fileUploadMaxFileSize / 1024 / 1024
- : 0;
- },
- maxFileUploadSizeMessage() {
- if (this.maxFileUploadSizeMB) {
- return (
- "Max file upload size is " +
- Math.round(this.maxFileUploadSizeMB) +
- " MB"
- );
- } else {
- return null;
- }
- },
- fileTooLarge() {
- return (
- this.settings &&
- this.settings.fileUploadMaxFileSize &&
- this.file &&
- this.file.size > this.settings.fileUploadMaxFileSize
- );
- },
- fileUploadState() {
- if (this.fileTooLarge) {
- return false;
- } else {
- return null;
- }
- },
- fileUploadInvalidFeedback() {
- if (this.fileTooLarge) {
- return (
- "File selected is larger than " + this.maxFileUploadSizeMB + " MB"
- );
- } else {
- return null;
- }
}
},
data() {
return {
dataProduct: null,
- file: null,
- isSelectingFile: false,
- settings: null,
fileContent: null
};
},
@@ -165,7 +97,6 @@ export default {
if (this.isDataProductURI) {
this.loadDataProduct(this.value);
}
- services.SettingsService.get().then(s => (this.settings = s));
},
methods: {
loadDataProduct(dataProductURI) {
@@ -195,20 +126,18 @@ export default {
.catch(utils.FetchUtils.reportError);
},
unselect() {
- this.file = null;
this.data = null;
this.valueChanged();
},
- fileSelected(dataProductURI) {
+ fileSelected(dataProductURI, dataProduct) {
this.data = dataProductURI;
- this.isSelectingFile = false;
- this.loadDataProduct(dataProductURI);
+ if (!dataProduct) {
+ this.loadDataProduct(dataProductURI);
+ } else {
+ this.dataProduct = dataProduct;
+ }
this.valueChanged();
},
- cancelFileSelection() {
- this.isSelectingFile = false;
- this.unselect();
- },
viewFile() {
this.fileContent = null;
fetch(this.dataProduct.downloadURL, {
@@ -219,12 +148,6 @@ export default {
this.fileContent = text;
this.$refs.modal.show();
});
- },
- uploadSuccess(result) {
- this.dataProduct = new models.DataProduct(result["data-product"]);
- this.data = this.dataProduct.productUri;
- this.valueChanged();
- this.$emit('uploadend');
}
}
};
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/InputFileSelector.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/InputFileSelector.vue
new file mode 100644
index 0000000..645a198
--- /dev/null
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/InputFileSelector.vue
@@ -0,0 +1,138 @@
+<template>
+ <div v-if="isSelectingFile">
+ <user-storage-file-selection-container
+ @file-selected="fileSelected"
+ @cancel="cancelFileSelection"
+ :selected-data-product-uris="selectedDataProductURIs"
+ />
+ </div>
+ <div
+ class="d-flex align-items-baseline"
+ v-else
+ >
+ <b-button
+ @click="isSelectingFile=true"
+ class="input-file-option"
+ >Select file from storage</b-button>
+ <span class="text-muted mx-3">OR</span>
+ <b-form-group
+ :description="maxFileUploadSizeMessage"
+ :state="fileUploadState"
+ :invalid-feedback="fileUploadInvalidFeedback"
+ class="input-file-option"
+ >
+ <uppy
+ ref="uppy"
+ xhr-upload-endpoint="/api/upload"
+ tus-upload-finish-endpoint="/api/tus-upload-finish"
+ @upload-success="uploadSuccess"
+ @upload-started="$emit('uploadstart')"
+ @upload-finished="uploadFinished"
+ :multiple="multiple"
+ />
+ </b-form-group>
+ </div>
+</template>
+
+<script>
+import { models, services } from "django-airavata-api";
+import UserStorageFileSelectionContainer from "../../storage/UserStorageFileSelectionContainer";
+import Uppy from "./Uppy";
+
+export default {
+ name: "input-file-selector",
+ props: {
+ multiple: {
+ type: Boolean,
+ default: false
+ },
+ selectedDataProductURIs: {
+ type: Array,
+ default: () => []
+ }
+ },
+ components: {
+ UserStorageFileSelectionContainer,
+ Uppy
+ },
+ computed: {
+ maxFileUploadSizeMB() {
+ return this.settings
+ ? this.settings.fileUploadMaxFileSize / 1024 / 1024
+ : 0;
+ },
+ maxFileUploadSizeMessage() {
+ if (this.maxFileUploadSizeMB) {
+ return (
+ "Max file upload size is " +
+ Math.round(this.maxFileUploadSizeMB) +
+ " MB"
+ );
+ } else {
+ return null;
+ }
+ },
+ fileTooLarge() {
+ return (
+ this.settings &&
+ this.settings.fileUploadMaxFileSize &&
+ this.file &&
+ this.file.size > this.settings.fileUploadMaxFileSize
+ );
+ },
+ fileUploadState() {
+ if (this.fileTooLarge) {
+ return false;
+ } else {
+ return null;
+ }
+ },
+ fileUploadInvalidFeedback() {
+ if (this.fileTooLarge) {
+ return (
+ "File selected is larger than " + this.maxFileUploadSizeMB + " MB"
+ );
+ } else {
+ return null;
+ }
+ }
+ },
+ data() {
+ return {
+ file: null,
+ isSelectingFile: false,
+ settings: null
+ };
+ },
+ created() {
+ services.SettingsService.get().then(s => (this.settings = s));
+ },
+ methods: {
+ unselect() {
+ this.file = null;
+ },
+ fileSelected(dataProductURI) {
+ this.isSelectingFile = false;
+ this.$emit("selected", dataProductURI);
+ },
+ cancelFileSelection() {
+ this.isSelectingFile = false;
+ this.unselect();
+ },
+ uploadSuccess(result) {
+ const dataProduct = new models.DataProduct(result["data-product"]);
+ this.$emit("selected", dataProduct.productUri, dataProduct);
+ },
+ uploadFinished() {
+ this.$emit('uploadend');
+ this.$refs.uppy.reset();
+ }
+ }
+};
+</script>
+
+<style scoped>
+.input-file-option {
+ flex: 1 1 50%;
+}
+</style>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/MultiFileInputEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/MultiFileInputEditor.vue
index 30204e3..38dfc47 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/MultiFileInputEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/MultiFileInputEditor.vue
@@ -2,25 +2,31 @@
<div>
<div
class="mb-2"
- v-for="fileEntry in fileEntries"
- :key="fileEntry.id"
+ v-for="dataProductURI in selectedDataProductURIs"
+ :key="dataProductURI"
>
<file-input-editor
- :value="fileEntry.value"
- :id="fileEntry.id"
+ :value="dataProductURI"
+ :id="dataProductURI"
:experiment="experiment"
:experiment-input="experimentInput"
- @input="updatedFile($event, fileEntry)"
- @uploadstart="uploadStart(fileEntry)"
- @uploadend="uploadEnd(fileEntry)"
+ @input="updatedFile($event, dataProductURI)"
/>
</div>
+ <input-file-selector
+ :selectedDataProductURIs="selectedDataProductURIs"
+ @selected="fileSelected"
+ @uploadstart="$emit('uploadstart')"
+ @uploadend="$emit('uploadend')"
+ multiple
+ />
</div>
</template>
<script>
import { InputEditorMixin } from "django-airavata-workspace-plugin-api";
import FileInputEditor from "./FileInputEditor.vue";
+import InputFileSelector from "./InputFileSelector";
export default {
name: "multi-file-input-editor",
@@ -31,38 +37,32 @@ export default {
}
},
components: {
- FileInputEditor
+ FileInputEditor,
+ InputFileSelector
},
data() {
- return {
- newFileCount: 0,
- fileEntries: this.createFileEntries(this.value)
- };
+ return {};
+ },
+ computed: {
+ selectedDataProductURIs() {
+ return this.createValueArray(this.value);
+ }
},
- computed: {},
methods: {
- addFile() {
- this.fileEntries.push({
- id: this.id + "-" + this.newFileCount++,
- value: null,
- uploading: false
- });
- },
- updatedFile(newValue, fileEntry) {
- if (!newValue && fileEntry.value) {
- this.removeFile(fileEntry);
- } else {
- fileEntry.value = newValue;
+ updatedFile(newValue, dataProductURI) {
+ // Only remove is handled here, input-file-selector handles adding
+ if (!newValue) {
+ this.removeFile(dataProductURI);
}
- this.data = this.fileEntries
- .filter(e => e.value) // exclude null entries
- .map(e => e.value)
- .join(",");
- this.valueChanged();
},
- removeFile(fileEntry) {
- const index = this.fileEntries.findIndex(e => e.id === fileEntry.id);
- this.fileEntries.splice(index, 1);
+ removeFile(dataProductURI) {
+ const index = this.selectedDataProductURIs.findIndex(
+ u => u === dataProductURI
+ );
+ const copyDataProductURIs = this.selectedDataProductURIs.slice();
+ copyDataProductURIs.splice(index, 1);
+ this.data = copyDataProductURIs.join(",");
+ this.valueChanged();
},
createValueArray(value) {
if (value && typeof value === "string") {
@@ -71,49 +71,11 @@ export default {
return [];
}
},
- createFileEntries(value) {
- const valueArray = this.createValueArray(value);
- const fileEntries = valueArray.map(v => {
- return {
- id: this.id + "-" + v,
- value: v,
- uploading: false
- };
- });
- // Add a null entry to accept an additional upload
- fileEntries.push(this.getNullFileEntry());
- return fileEntries;
- },
- getNullFileEntry() {
- // Reuse the old null entry if it exists, otherwise create a new one
- if (
- this.fileEntries &&
- this.fileEntries.length > 0 &&
- this.fileEntries[this.fileEntries.length - 1].value === null
- ) {
- return this.fileEntries[this.fileEntries.length - 1];
- } else {
- return {
- id: this.id + "-" + this.newFileCount++,
- value: null,
- uploading: false
- };
- }
- },
- uploadStart(fileEntry) {
- fileEntry.uploading = true;
- this.$emit("uploadstart");
- },
- uploadEnd(fileEntry) {
- fileEntry.uploading = false;
- if (this.fileEntries.every(fe => !fe.uploading)) {
- this.$emit("uploadend");
- }
- }
- },
- watch: {
- value(newValue) {
- this.fileEntries = this.createFileEntries(newValue);
+ fileSelected(dataProductURI) {
+ const values = this.createValueArray(this.value);
+ values.push(dataProductURI);
+ this.data = values.join(",");
+ this.valueChanged();
}
}
};
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue
index 98c96c5..39cfb2f 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/Uppy.vue
@@ -1,6 +1,6 @@
<template>
- <div>
- <div ref="fileInput" />
+ <div class="custom-Uppy">
+ <div ref="dragDrop" />
<div ref="statusBar" />
</div>
</template>
@@ -9,17 +9,15 @@
import { services, utils } from "django-airavata-api";
import Uppy from "@uppy/core";
-import FileInput from "@uppy/file-input";
+import DragDrop from "@uppy/drag-drop";
import StatusBar from "@uppy/status-bar";
import Tus from "@uppy/tus";
import XHRUpload from "@uppy/xhr-upload";
-import "@uppy/core/dist/style.css";
-import "@uppy/status-bar/dist/style.css";
-import "@uppy/file-input/dist/style.css";
+import "@uppy/core/dist/style.min.css";
+import "@uppy/status-bar/dist/style.min.css";
+import "@uppy/drag-drop/dist/style.min.css";
-// TODO: dispatch upload start
-// TODO: maybe use dragdrop UI?
export default {
name: "uppy",
props: {
@@ -32,6 +30,10 @@ export default {
tusUploadFinishEndpoint: {
type: String,
required: false
+ },
+ multiple: {
+ type: Boolean,
+ default: false
}
},
mounted() {
@@ -46,7 +48,7 @@ export default {
data() {
return {
uppy: null
- }
+ };
},
methods: {
initUppy() {
@@ -54,9 +56,13 @@ export default {
// TODO: set id
autoProceed: true,
// TODO: add maxFileSize restriction
- debug: true
+ debug: true,
+ restrictions: {
+ maxNumberOfFiles: this.multiple ? null : 1,
+ }
});
- this.uppy.use(FileInput, { target: this.$refs.fileInput, pretty: false });
+ // TODO: add 'note' here as passed in through prop (Max file upload size is 64MB)
+ this.uppy.use(DragDrop, { target: this.$refs.dragDrop });
this.uppy.use(StatusBar, {
target: this.$refs.statusBar,
hideUploadButton: true,
@@ -78,9 +84,9 @@ export default {
endpoint: this.xhrUploadEndpoint,
withCredentials: true,
headers: {
- 'X-CSRFToken': utils.FetchUtils.getCSRFToken()
+ "X-CSRFToken": utils.FetchUtils.getCSRFToken()
},
- fieldName: 'file'
+ fieldName: "file"
});
this.uppy.on("upload-success", (file, response) => {
this.$emit("upload-success", response.body);
@@ -99,3 +105,15 @@ export default {
}
};
</script>
+
+<style scoped>
+.custom-Uppy >>> .uppy-DragDrop-inner {
+ padding: 5px 0px;
+}
+.custom-Uppy >>> .UppyIcon {
+ display: none;
+}
+.custom-Uppy >>> .uppy-DragDrop-label {
+ margin-bottom: 0px;
+}
+</style>
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
index a47ff71..b7f34ef 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
@@ -22,6 +22,7 @@
:xhr-upload-endpoint="uploadEndpoint"
:tus-upload-finish-endpoint="uploadEndpoint"
@upload-success="uploadSuccess"
+ multiple
/>
</b-form-group>
</div>