diff --git a/services/web/client/source/class/osparc/component/editor/ClusterEditor.js b/services/web/client/source/class/osparc/component/editor/ClusterEditor.js index bb238a5ba08..2c152764050 100644 --- a/services/web/client/source/class/osparc/component/editor/ClusterEditor.js +++ b/services/web/client/source/class/osparc/component/editor/ClusterEditor.js @@ -121,9 +121,9 @@ qx.Class.define("osparc.component.editor.ClusterEditor", { control = new qx.ui.form.TextField().set({ font: "text-14", backgroundColor: "background-main", - placeholder: this.tr("Username"), - height: 35 + placeholder: this.tr("Username") }); + control.getContentElement().setAttribute("autocomplete", "off"); this.bind("simpleAuthenticationUsername", control, "value"); control.bind("value", this, "simpleAuthenticationUsername"); control.setRequired(true); @@ -135,9 +135,9 @@ qx.Class.define("osparc.component.editor.ClusterEditor", { control = new qx.ui.form.PasswordField().set({ font: "text-14", backgroundColor: "background-main", - placeholder: this.tr("Password"), - height: 35 + placeholder: this.tr("Password") }); + control.getContentElement().setAttribute("autocomplete", "off"); this.bind("simpleAuthenticationPassword", control, "value"); control.bind("value", this, "simpleAuthenticationPassword"); control.setRequired(true); diff --git a/services/web/client/source/class/osparc/component/workbench/SvgWidget.js b/services/web/client/source/class/osparc/component/workbench/SvgWidget.js index 2881ad0bff5..0da8d082868 100644 --- a/services/web/client/source/class/osparc/component/workbench/SvgWidget.js +++ b/services/web/client/source/class/osparc/component/workbench/SvgWidget.js @@ -113,7 +113,7 @@ qx.Class.define("osparc.component.workbench.SvgWidget", { return osparc.wrapper.Svg.drawCurve(this.__canvas, controls, dashed); }, - drawDashedRect: function(width, height, x, y) { + drawDashedRect: function(width, height, x = 0, y = 0) { return osparc.wrapper.Svg.drawDashedRect(this.__canvas, width, height, x, y); } } diff --git a/services/web/client/source/class/osparc/component/workbench/WorkbenchUI.js b/services/web/client/source/class/osparc/component/workbench/WorkbenchUI.js index fff4b0cfc30..ab938f7098e 100644 --- a/services/web/client/source/class/osparc/component/workbench/WorkbenchUI.js +++ b/services/web/client/source/class/osparc/component/workbench/WorkbenchUI.js @@ -127,9 +127,10 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { __pointerPos: null, __selectedItemId: null, __startHint: null, - __dropHint: null, + __dropMe: null, __panning: null, - __draggingFile: null, + __isDraggingFile: null, + __isDraggingLink: null, __applyStudy: function(study) { study.getWorkbench().addListener("reloadModel", () => { @@ -828,11 +829,7 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { }; }, - __pointerEventToWorkbenchPos: function(e) { - const { - x, - y - } = this.__pointerEventToScreenPos(e); + __screenToToWorkbenchPos: function(x, y) { const scaledPos = this.__scaleCoordinates(x, y); const scrollX = this._workbenchLayoutScroll.getScrollX(); const scrollY = this._workbenchLayoutScroll.getScrollY(); @@ -843,6 +840,14 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { }; }, + __pointerEventToWorkbenchPos: function(e) { + const { + x, + y + } = this.__pointerEventToScreenPos(e); + return this.__screenToToWorkbenchPos(x, y); + }, + __updateTempEdge: function(e) { let nodeUI = null; if (this.__tempEdgeNodeId !== null) { @@ -1175,16 +1180,6 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { contextMenu.addButtons(buttons); contextMenu.setPos(e.getDocumentLeft() - contextMenu.w2, e.getDocumentTop() - contextMenu.h2); contextMenu.show(); - /* - const tapListener = ev => { - if (osparc.utils.Utils.isMouseOnElement(contextMenu, ev)) { - return; - } - contextMenu.hide(); - document.removeEventListener("mousedown", tapListener); - }; - document.addEventListener("mousedown", tapListener); - */ }, __mouseDown: function(e) { @@ -1210,17 +1205,19 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { this.set({ cursor: "move" }); - } else if (this.__draggingFile) { - this.__dragging(e, true); + } else if (this.__isDraggingLink) { + this.__draggingLink(e, true); } }, - __mouseUp: function() { + __mouseUp: function(e) { if (this.__panning) { this.__panning = false; this.set({ cursor: "auto" }); + } else if (this.__isDraggingLink) { + this.__dropLink(e); } }, @@ -1440,25 +1437,22 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { ].forEach(signalName => { domEl.addEventListener(signalName, e => { const dragging = signalName !== "dragleave"; - this.__dragging(e, dragging); + this.__draggingFile(e, dragging); }, this); }); - domEl.addEventListener("drop", this.__drop.bind(this), false); + domEl.addEventListener("drop", this.__dropFile.bind(this), false); this.setDroppable(true); - [ - "dragover", // on target (pointer over) - "dragleave" // on target (pointer out) - ].forEach(signalName => { - this.addListener(signalName, e => { - const dragging = signalName !== "dragleave"; - if (dragging === false) { - this.__draggingFile = dragging; - } - this.__dragging(e, dragging); - }, this); - }); - this.addListener("drop", this.__drop.bind(this), false); + const stopDragging = e => { + this.__isDraggingLink = null; + this.__updateWidgets(false); + }; + const startDragging = e => { + this.addListenerOnce("dragleave", stopDragging, this); + this.addListenerOnce("dragover", startDragging, this); + this.__draggingLink(e, true); + }; + this.addListenerOnce("dragover", startDragging, this); this.addListener("mousewheel", this.__mouseWheel, this); this.addListener("mousedown", this.__mouseDown, this); @@ -1484,25 +1478,38 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { this.__workbenchLayout.addListener("resize", () => this.__updateHint(), this); }, - __allowDrag: function(e) { + __allowDragFile: function(e) { let allow = false; - if (this.__draggingFile) { + if (this.__isDraggingFile) { // item still being dragged allow = true; - } else if ("supportsType" in e) { - // item drag from osparc's file tree - allow = e.supportsType("osparc-file-link"); - this.__draggingFile = allow; } else { // item drag from the outside world allow = e.target instanceof SVGElement; - this.__draggingFile = allow; + this.__isDraggingFile = allow; + } + return allow; + }, + + __allowDragLink: function(e) { + let allow = false; + if (this.__isDraggingLink) { + // item still being dragged + allow = true; + } else if ("supportsType" in e) { + // item drag from osparc's file tree + allow = e.supportsType("osparc-file-link"); + if (allow) { + // store "osparc-file-link" data in variable, + // because the mousemove event doesn't contain that information + this.__isDraggingLink = e.getData("osparc-file-link"); + } } return allow; }, - __dragging: function(e, dragging) { - if (this.__allowDrag(e)) { + __draggingFile: function(e, dragging) { + if (this.__allowDragFile(e)) { e.preventDefault(); e.stopPropagation(); } else { @@ -1512,45 +1519,61 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { if (!this.isPropertyInitialized("study") || this.getStudy().isReadOnly()) { return; } - let posX = 0; - let posY = 0; - if ("offsetX" in e && "offsetY" in e) { - posX = e.offsetX + 2; - posY = e.offsetY + 2; + + const posX = e.offsetX + 2; + const posY = e.offsetY + 2; + this.__updateWidgets(dragging, posX, posY); + }, + + __draggingLink: function(e, dragging) { + if (this.__allowDragLink(e)) { + e.preventDefault(); + e.stopPropagation(); } else { - const pos = this.__pointerEventToWorkbenchPos(e); - posX = pos.x; - posY = pos.y; + dragging = false; } - if (this.__dropHint === null) { - const dropHint = this.__dropHint = new qx.ui.basic.Label(this.tr("Drop me")).set({ + if (!this.isPropertyInitialized("study") || this.getStudy().isReadOnly()) { + return; + } + + const pos = this.__pointerEventToWorkbenchPos(e); + this.__updateWidgets(dragging, pos.x, pos.y); + }, + + __updateWidgets: function(dragging, posX, posY) { + const boxWidth = 120; + const boxHeight = 60; + if (this.__dropMe === null) { + const dropHint = this.__dropMe = new qx.ui.basic.Label(this.tr("Drop me")).set({ font: "workbench-start-hint", textColor: "workbench-start-hint" }); dropHint.exclude(); this.__workbenchLayout.add(dropHint); - const nodeWidth = osparc.component.workbench.NodeUI.NODE_WIDTH; - const nodeHeight = osparc.component.workbench.NodeUI.NODE_HEIGHT; - dropHint.rect = this.__svgLayer.drawDashedRect(nodeWidth, nodeHeight, posX, posY); + dropHint.rect = this.__svgLayer.drawDashedRect(boxWidth, boxHeight); } + const dropMe = this.__dropMe; if (dragging) { - this.__dropHint.show(); - this.__dropHint.setLayoutProperties({ - left: posX, - top: posY + dropMe.show(); + const dropMeBounds = dropMe.getBounds() || dropMe.getSizeHint(); + dropMe.setLayoutProperties({ + left: posX - parseInt(dropMeBounds.width/2) - parseInt(boxWidth/2), + top: posY - parseInt(dropMeBounds.height/2)- parseInt(boxHeight/2) }); - osparc.component.workbench.SvgWidget.updateRect(this.__dropHint.rect, posX, posY); + if ("rect" in dropMe) { + osparc.component.workbench.SvgWidget.updateRect(dropMe.rect, posX - boxWidth, posY - boxHeight); + } } else { this.__removeDropHint(); } }, - __drop: function(e) { - this.__dragging(e, false); + __dropFile: function(e) { + this.__draggingFile(e, false); if ("dataTransfer" in e) { - this.__draggingFile = false; + this.__isDraggingFile = false; const files = e.dataTransfer.files; if (files.length === 1) { const pos = { @@ -1568,9 +1591,14 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { } else { osparc.component.message.FlashMessenger.getInstance().logAs(this.tr("Only one file is accepted"), "ERROR"); } - } else if ("supportsType" in e && e.supportsType("osparc-file-link")) { - this.__draggingFile = false; - const data = e.getData("osparc-file-link")["dragData"]; + } + }, + + __dropLink: function(e) { + this.__draggingLink(e, false); + + if (this.__isDraggingLink && "dragData" in this.__isDraggingLink) { + const data = this.__isDraggingLink["dragData"]; const pos = this.__pointerEventToWorkbenchPos(e, false); const service = qx.data.marshal.Json.createModel(osparc.utils.Services.getFilePicker()); const nodeUI = this.__addNode(service, pos); @@ -1578,6 +1606,7 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { const filePicker = new osparc.file.FilePicker(node); filePicker.buildLayout(); osparc.file.FilePicker.setOutputValueFromStore(node, data.getLocation(), data.getDatasetId(), data.getFileId(), data.getLabel()); + this.__isDraggingLink = null; } }, @@ -1601,9 +1630,9 @@ qx.Class.define("osparc.component.workbench.WorkbenchUI", { }, __removeDropHint: function() { - this.__dropHint.setVisibility("excluded"); - osparc.component.workbench.SvgWidget.removeRect(this.__dropHint.rect); - this.__dropHint = null; + this.__dropMe.setVisibility("excluded"); + osparc.component.workbench.SvgWidget.removeRect(this.__dropMe.rect); + this.__dropMe = null; } } }); diff --git a/services/web/client/source/class/osparc/data/model/Study.js b/services/web/client/source/class/osparc/data/model/Study.js index dbdd04abd65..ca029fc7fd9 100644 --- a/services/web/client/source/class/osparc/data/model/Study.js +++ b/services/web/client/source/class/osparc/data/model/Study.js @@ -403,7 +403,7 @@ qx.Class.define("osparc.data.model.Study", { }, updateStudy: function(params, run = false) { - return new Promise(resolve => { + return new Promise((resolve, reject) => { osparc.data.Resources.fetch("studies", "put", { url: { "studyId": this.getUuid(), @@ -413,11 +413,13 @@ qx.Class.define("osparc.data.model.Study", { ...this.serialize(), ...params } - }).then(data => { - this.__updateModel(data); - qx.event.message.Bus.getInstance().dispatchByName("updateStudy", data); - resolve(data); - }); + }) + .then(data => { + this.__updateModel(data); + qx.event.message.Bus.getInstance().dispatchByName("updateStudy", data); + resolve(data); + }) + .catch(err => reject(err)); }); }, diff --git a/services/web/client/source/class/osparc/desktop/WorkbenchView.js b/services/web/client/source/class/osparc/desktop/WorkbenchView.js index 6b5ec8e0ddc..b4bac2f04f6 100644 --- a/services/web/client/source/class/osparc/desktop/WorkbenchView.js +++ b/services/web/client/source/class/osparc/desktop/WorkbenchView.js @@ -83,6 +83,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { members: { __sidePanels: null, + __nodesPage: null, __studyTreeItem: null, __nodesTree: null, __filesTree: null, @@ -335,7 +336,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", { })); homeAndNodesTree.add(addNewNodeBtn); - const nodesPage = this.__createTabPage("@FontAwesome5Solid/list", this.tr("Nodes"), homeAndNodesTree, primaryColumnBGColor); + const nodesPage = this.__nodesPage = this.__createTabPage("@FontAwesome5Solid/list", this.tr("Nodes"), homeAndNodesTree, primaryColumnBGColor); tabViewPrimary.add(nodesPage); const filesTree = this.__filesTree = new osparc.file.FilesTree().set({ @@ -798,6 +799,9 @@ qx.Class.define("osparc.desktop.WorkbenchView", { page.getChildControl("button").exclude(); }); + const tabViewLeftPanel = this.getChildControl("side-panel-left-tabs"); + tabViewLeftPanel.setSelection([this.__nodesPage]); + if (node instanceof osparc.data.model.Study) { this.__populateSecondPanelStudy(node); } else if (node && node.isFilePicker()) { @@ -924,22 +928,42 @@ qx.Class.define("osparc.desktop.WorkbenchView", { view.setEnabled(false); this.__infoPage.add(view); } else { + // empty File Picker + const tabViewLeftPanel = this.getChildControl("side-panel-left-tabs"); + tabViewLeftPanel.setSelection([this.__storagePage]); + this.__settingsPage.getChildControl("button").show(); this.getChildControl("side-panel-right-tabs").setSelection([this.__settingsPage]); const filePickerView = new osparc.file.FilePicker(filePicker); filePickerView.buildLayout(); - filePickerView.getChildControl("reload-button").exclude(); - filePickerView.getChildControl("files-tree").set({ - hideRoot: true, - showLeafs: true + + const fileDrop = new osparc.file.FileDrop(); + fileDrop.addListener("localFileDropped", e => { + const files = e.getData()["data"]; + if (filePickerView.uploadPendingFiles(files)) { + setTimeout(() => this.__populateSecondPanel(filePicker), 500); + } + fileDrop.resetDropAction(); }); - filePickerView.getChildControl("folder-viewer").exclude(); - filePickerView.getChildControl("selected-file-layout").getChildControl("download-button").exclude(); - filePickerView.addListener("itemSelected", () => this.__populateSecondPanel(filePicker)); - this.__settingsPage.add(filePickerView, { + fileDrop.addListener("fileLinkDropped", e => { + const data = e.getData()["data"]; + osparc.file.FilePicker.setOutputValueFromStore(filePicker, data.getLocation(), data.getDatasetId(), data.getFileId(), data.getLabel()); + this.__populateSecondPanel(filePicker); + fileDrop.resetDropAction(); + }); + + this.__settingsPage.add(fileDrop, { flex: 1 }); + + filePickerView.getChildControl("reload-button").exclude(); + filePickerView.getChildControl("files-tree").exclude(); + filePickerView.getChildControl("folder-viewer").exclude(); + filePickerView.getChildControl("selected-file-layout").exclude(); + filePickerView.getChildControl("select-button").exclude(); + filePickerView.addListener("itemSelected", () => this.__populateSecondPanel(filePicker)); + this.__settingsPage.add(filePickerView); } }, diff --git a/services/web/client/source/class/osparc/file/FileDrop.js b/services/web/client/source/class/osparc/file/FileDrop.js new file mode 100644 index 00000000000..708e3316b7a --- /dev/null +++ b/services/web/client/source/class/osparc/file/FileDrop.js @@ -0,0 +1,327 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2021 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +/** + * @ignore(SVGElement) + */ + +qx.Class.define("osparc.file.FileDrop", { + extend: qx.ui.core.Widget, + + construct: function() { + this.base(arguments); + + this._setLayout(new qx.ui.layout.Canvas()); + + const contentElement = this.getContentElement(); + contentElement.setStyles(this.self().getBorderStyle()); + const colorManager = qx.theme.manager.Color.getInstance(); + colorManager.addListener("changeTheme", () => { + if (this.getShowBorder()) { + contentElement.setStyles(this.self().getBorderStyle()); + } + }, this); + + const dropHere = this.__dropHere = new qx.ui.basic.Label(this.tr("Drop file here")).set({ + font: "title-14", + alignX: "center", + alignY: "middle" + }); + this._add(dropHere, { + top: 40, + left: 40 + }); + dropHere.addListener("appear", () => { + // center it + const dropHereBounds = dropHere.getBounds() || dropHere.getSizeHint(); + const { + height, + width + } = this.getBounds(); + dropHere.setLayoutProperties({ + top: parseInt((height - dropHereBounds.height) / 2), + left: parseInt((width - dropHereBounds.width) / 2) + }); + }, this); + + const svgLayer = this.__svgLayer = new osparc.component.workbench.SvgWidget(); + this._add(svgLayer, { + top: 0, + right: 0, + bottom: 0, + left: 0 + }); + + this.__addDragAndDropListeners(); + }, + + statics: { + getBorderStyle: function() { + return { + "border-width": "3px", + "border-radius": "20px", + "border-color": qx.theme.manager.Color.getInstance().resolve("contrasted-background+"), + "border-style": "dotted" + }; + }, + + getNoBorderStyle: function() { + return { + "border-width": "0px" + }; + } + }, + + events: { + "localFileDropped": "qx.event.type.Data", + "fileLinkDropped": "qx.event.type.Data" + }, + + properties: { + showBorder: { + check: "Boolean", + init: true, + apply: "__applyShowBorder" + }, + + showDropHere: { + check: "Boolean", + init: true, + apply: "__applyShowDropHere" + } + }, + + members: { + __svgLayer: null, + __dropHere: null, + __dropMe: null, + __isDraggingFile: null, + __isDraggingLink: null, + + __applyShowBorder: function(show) { + const contentElement = this.getContentElement(); + contentElement.setStyles(show ? this.self().getBorderStyle() : this.self().getNoBorderStyle()); + }, + + __applyShowDropHere: function(value) { + this.__dropHere.setVisibility(value ? "visible" : "excluded"); + }, + + resetDropAction: function() { + this.__updateWidgets(false); + }, + + __pointerFileEventToScreenPos: function(e) { + const rect = this.getContentElement().getDomElement().getBoundingClientRect(); + return { + x: e.pageX - rect.x, + y: e.pageY - rect.y + }; + }, + + __pointerLinkEventToScreenPos: function(e) { + const rect = this.getContentElement().getDomElement().getBoundingClientRect(); + return { + x: e.getDocumentLeft() - rect.x, + y: e.getDocumentTop() - rect.y + }; + }, + + __allowDragFile: function(e) { + let allow = false; + if (this.__isDraggingFile) { + // item still being dragged + allow = true; + } else { + allow = e.target instanceof SVGElement; + this.__isDraggingFile = allow; + } + return allow; + }, + + __allowDragLink: function(e) { + let allow = false; + if (this.__isDraggingLink) { + // item still being dragged + allow = true; + } else if ("supportsType" in e) { + // item drag from osparc's file tree + allow = e.supportsType("osparc-file-link"); + if (allow) { + // store "osparc-file-link" data in variable, + // because the mousemove event doesn't contain that information + this.__isDraggingLink = e.getData("osparc-file-link"); + } + } + return allow; + }, + + __draggingFile: function(e, dragging) { + if (this.__allowDragFile(e)) { + e.preventDefault(); + e.stopPropagation(); + } else { + dragging = false; + } + + const pos = this.__pointerFileEventToScreenPos(e); + this.__updateWidgets(dragging, pos.x, pos.y); + }, + + __draggingLink: function(e, dragging) { + if (this.__allowDragLink(e)) { + e.preventDefault(); + e.stopPropagation(); + } else { + dragging = false; + } + + const pos = this.__pointerLinkEventToScreenPos(e); + this.__updateWidgets(dragging, pos.x, pos.y); + }, + + __updateWidgets: function(dragging, posX, posY) { + if (dragging) { + this.__dropHere.exclude(); + this.__updateDropMe(posX, posY); + } else { + if (this.getShowDropHere()) { + this.__dropHere.show(); + } + this.__hideDropMe(); + } + }, + + __updateDropMe: function(posX, posY) { + const boxWidth = 120; + const boxHeight = 60; + if (this.__dropMe === null) { + this.__dropMe = new qx.ui.basic.Label(this.tr("Drop me")).set({ + font: "title-14", + textAlign: "center" + }); + this._add(this.__dropMe); + const svgLayer = this.__svgLayer; + if (svgLayer.getReady()) { + this.__dropMe.rect = svgLayer.drawDashedRect(boxWidth, boxHeight); + } else { + svgLayer.addListenerOnce("SvgWidgetReady", () => this.__dropMe.rect = svgLayer.drawDashedRect(boxWidth, boxHeight), this); + } + } + const dropMe = this.__dropMe; + dropMe.show(); + const dropMeBounds = dropMe.getBounds() || dropMe.getSizeHint(); + dropMe.setLayoutProperties({ + left: posX - parseInt(dropMeBounds.width/2) - parseInt(boxWidth/2), + top: posY - parseInt(dropMeBounds.height/2)- parseInt(boxHeight/2) + }); + if ("rect" in dropMe) { + dropMe.rect.stroke({ + width: 1 + }); + osparc.component.workbench.SvgWidget.updateRect(dropMe.rect, posX - boxWidth, posY - boxHeight); + } + }, + + __hideDropMe: function() { + const dropMe = this.__dropMe; + if (dropMe) { + if ("rect" in dropMe) { + dropMe.rect.stroke({ + width: 0 + }); + } + dropMe.exclude(); + } + }, + + __dropFile: function(e) { + this.__draggingFile(e, false); + + this.__isDraggingFile = false; + if ("dataTransfer" in e) { + const files = e.dataTransfer.files; + if (files.length === 1) { + const fileList = e.dataTransfer.files; + if (fileList.length) { + this.fireDataEvent("localFileDropped", { + data: files, + pos: this.__pointerFileEventToScreenPos(e) + }); + } + } else { + osparc.component.message.FlashMessenger.getInstance().logAs(this.tr("Only one file is accepted"), "ERROR"); + } + } + }, + + __dropLink: function(e) { + this.__draggingLink(e, false); + + if (this.__isDraggingLink && "dragData" in this.__isDraggingLink) { + this.fireDataEvent("fileLinkDropped", { + data: this.__isDraggingLink["dragData"], + pos: this.__pointerLinkEventToScreenPos(e) + }); + this.__isDraggingLink = null; + } + }, + + __addDragAndDropListeners: function() { + this.addListenerOnce("appear", () => { + // listen to drag&drop files from local-storage + const domEl = this.getContentElement().getDomElement(); + [ + "dragenter", + "dragover", + "dragleave" + ].forEach(signalName => { + domEl.addEventListener(signalName, e => { + const dragging = signalName !== "dragleave"; + this.__draggingFile(e, dragging); + }, this); + }); + domEl.addEventListener("drop", this.__dropFile.bind(this), false); + + // listen to drag&drop file-links from osparc-storage + this.setDroppable(true); + + const stopDraggingLink = () => { + this.__isDraggingLink = null; + this.__updateWidgets(false); + }; + const startDraggingLink = e => { + this.addListenerOnce("dragleave", stopDraggingLink, this); + this.addListenerOnce("dragover", startDraggingLink, this); + this.__draggingLink(e, true); + }; + this.addListenerOnce("dragover", startDraggingLink, this); + + this.addListener("mousemove", e => { + if (this.__isDraggingLink) { + this.__draggingLink(e, true); + } + }, this); + this.addListener("mouseup", e => { + if (this.__isDraggingLink) { + this.__dropLink(e, true); + } + }, this); + }, this); + } + } +}); diff --git a/services/web/client/source/class/osparc/file/FilePicker.js b/services/web/client/source/class/osparc/file/FilePicker.js index 7396cf37560..c2f1ff3089b 100644 --- a/services/web/client/source/class/osparc/file/FilePicker.js +++ b/services/web/client/source/class/osparc/file/FilePicker.js @@ -400,11 +400,13 @@ qx.Class.define("osparc.file.FilePicker", { uploadPendingFiles: function(files) { if (files.length > 0) { - if (files.length > 1) { - osparc.component.message.FlashMessenger.getInstance().logAs(this.tr("Only one file is accepted"), "ERROR"); + if (files.length === 1) { + this.getChildControl("files-add").retrieveUrlAndUpload(files[0]); + return true; } - this.getChildControl("files-add").retrieveUrlAndUpload(files[0]); + osparc.component.message.FlashMessenger.getInstance().logAs(this.tr("Only one file is accepted"), "ERROR"); } + return false; }, __selectionChanged: function(selectedItem) { diff --git a/services/web/client/source/class/osparc/studycard/Medium.js b/services/web/client/source/class/osparc/studycard/Medium.js index 05b5719a3f0..e339dd3951b 100644 --- a/services/web/client/source/class/osparc/studycard/Medium.js +++ b/services/web/client/source/class/osparc/studycard/Medium.js @@ -105,8 +105,7 @@ qx.Class.define("osparc.studycard.Medium", { this._add(extraInfoLayout); thumbnailWidth = Math.min(thumbnailWidth, this.self().THUMBNAIL_MAX_WIDTH); const thumbnail = this.__createThumbnail(thumbnailWidth, maxThumbnailHeight); - if (thumbnail.getChildControl("image").getSource() !== osparc.dashboard.GridButtonItem.STUDY_ICON) { - // Only show if not default thumbnail + if (thumbnail) { this._add(thumbnail); } } else { @@ -117,8 +116,7 @@ qx.Class.define("osparc.studycard.Medium", { thumbnailWidth -= this.self().EXTRA_INFO_WIDTH; thumbnailWidth = Math.min(thumbnailWidth, this.self().THUMBNAIL_MAX_WIDTH); const thumbnail = this.__createThumbnail(thumbnailWidth, maxThumbnailHeight); - if (thumbnail.getChildControl("image").getSource() !== osparc.dashboard.GridButtonItem.STUDY_ICON) { - // Only show if not default thumbnail + if (thumbnail) { hBox.add(thumbnail, { flex: 1 }); @@ -126,7 +124,10 @@ qx.Class.define("osparc.studycard.Medium", { this._add(hBox); } - this._add(this.__createDescription()); + const description = this.__createDescription(); + if (description) { + this._add(description); + } }, __createMenuButton: function() { @@ -228,12 +229,18 @@ qx.Class.define("osparc.studycard.Medium", { }, __createThumbnail: function(maxWidth, maxHeight = 150) { - return osparc.studycard.Utils.createThumbnail(this.getStudy(), maxWidth, maxHeight); + if (this.getStudy().getThumbnail()) { + return osparc.studycard.Utils.createThumbnail(this.getStudy(), maxWidth, maxHeight); + } + return null; }, __createDescription: function() { - const maxHeight = 300; - return osparc.studycard.Utils.createDescription(this.getStudy(), maxHeight); + if (this.getStudy().getDescription()) { + const maxHeight = 300; + return osparc.studycard.Utils.createDescription(this.getStudy(), maxHeight); + } + return null; }, __openAccessRights: function() { diff --git a/services/web/client/source/class/osparc/theme/ColorLight.js b/services/web/client/source/class/osparc/theme/ColorLight.js index b507f9b507b..c8fe6d0a712 100644 --- a/services/web/client/source/class/osparc/theme/ColorLight.js +++ b/services/web/client/source/class/osparc/theme/ColorLight.js @@ -22,6 +22,10 @@ qx.Theme.define("osparc.theme.ColorLight", { "loading-page-background-color": "background-main", "loading-page-text": "#000000", - "loading-page-spinner": "#222222" + "loading-page-spinner": "#222222", + + "contrasted-background++": "#BBB", + "scrollbar-passive": "contrasted-background+", + "scrollbar-active": "contrasted-background++" } });