diff --git a/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js b/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js index 56972d7eb6f..fdbcebeaec2 100644 --- a/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js +++ b/services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js @@ -140,6 +140,8 @@ qx.Class.define("osparc.auth.ui.LoginView", { `; } const disclaimer = osparc.announcement.AnnouncementUIFactory.createLoginAnnouncement(this.tr("Disclaimer"), text); + disclaimer.getChildren()[0].setFont("text-14"); // title + disclaimer.getChildren()[1].setFont("text-12"); // description this.add(disclaimer); this.add(new qx.ui.core.Spacer(), { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js index 0d058644bce..c9171ebc2e2 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardBase.js @@ -16,7 +16,7 @@ ************************************************************************ */ qx.Class.define("osparc.dashboard.CardBase", { - extend: qx.ui.form.ToggleButton, + extend: qx.ui.core.Widget, implement: [qx.ui.form.IModel, osparc.filter.IFilterable], include: [qx.ui.form.MModelProperty, osparc.filter.MFilterable], type: "abstract", @@ -33,6 +33,8 @@ qx.Class.define("osparc.dashboard.CardBase", { "pointerout", "focusout" ].forEach(e => this.addListener(e, this._onPointerOut, this)); + + this.addListener("changeSelected", this.__evalSelectedButton, this); }, events: { @@ -237,6 +239,20 @@ qx.Class.define("osparc.dashboard.CardBase", { nullable: true }, + selected: { + check: "Boolean", + init: false, + nullable: false, + event: "changeSelected", + }, + + icon: { + check: "String", + init: null, + nullable: true, + apply: "_applyIcon", + }, + resourceData: { check: "Object", nullable: false, @@ -246,7 +262,8 @@ qx.Class.define("osparc.dashboard.CardBase", { resourceType: { check: ["study", "template", "service"], - nullable: false, + init: true, + nullable: true, event: "changeResourceType" }, @@ -365,7 +382,7 @@ qx.Class.define("osparc.dashboard.CardBase", { check: "Boolean", init: false, nullable: false, - apply: "_applyMultiSelectionMode" + apply: "__applyMultiSelectionMode" }, fetching: { @@ -444,6 +461,35 @@ qx.Class.define("osparc.dashboard.CardBase", { }); }, + __applyMultiSelectionMode: function(value) { + if (!value) { + this.setSelected(false); + } + this.__evalSelectedButton(); + }, + + __evalSelectedButton: function() { + if ( + this.hasChildControl("menu-button") && + this.hasChildControl("tick-selected") && + this.hasChildControl("tick-unselected") + ) { + const menuButton = this.getChildControl("menu-button"); + const tick = this.getChildControl("tick-selected"); + const untick = this.getChildControl("tick-unselected"); + if (this.isResourceType("study") && this.isMultiSelectionMode()) { + const selected = this.getSelected(); + menuButton.setVisibility("excluded"); + tick.setVisibility(selected ? "visible" : "excluded"); + untick.setVisibility(selected ? "excluded" : "visible"); + } else { + menuButton.setVisibility("visible"); + tick.setVisibility("excluded"); + untick.setVisibility("excluded"); + } + } + }, + __applyUuid: function(value, old) { const resourceType = this.getResourceType() || "study"; osparc.utils.Utils.setIdToWidget(this, resourceType + "BrowserListItem_" + value); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ToggleButtonContainer.js b/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js similarity index 67% rename from services/static-webserver/client/source/class/osparc/dashboard/ToggleButtonContainer.js rename to services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js index bbabe433161..047b047e8f7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ToggleButtonContainer.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/CardContainer.js @@ -6,9 +6,9 @@ */ /** - * Container for GridButtonItems and ListButtonItems (ToggleButtons), with some convenient methods. + * Container for GridButtons and ListButtons (CardBase, FolderButtonBase and WorkspaceButtonBase), with some convenient methods. */ -qx.Class.define("osparc.dashboard.ToggleButtonContainer", { +qx.Class.define("osparc.dashboard.CardContainer", { extend: qx.ui.container.Composite, construct: function() { @@ -22,20 +22,30 @@ qx.Class.define("osparc.dashboard.ToggleButtonContainer", { "changeVisibility": "qx.event.type.Data" }, + statics: { + isValidCard: function(widget) { + return ( + widget instanceof osparc.dashboard.CardBase || + widget instanceof osparc.dashboard.FolderButtonBase || + widget instanceof osparc.dashboard.WorkspaceButtonBase + ); + }, + }, + members: { __lastSelectedIdx: null, // overridden add: function(child, options) { - if (child instanceof qx.ui.form.ToggleButton) { + if (this.self().isValidCard(child)) { if (osparc.dashboard.ResourceContainerManager.cardExists(this, child)) { return; } this.base(arguments, child, options); - child.addListener("changeValue", () => this.fireDataEvent("changeSelection", this.getSelection()), this); + child.addListener("changeSelected", () => this.fireDataEvent("changeSelection", this.getSelection()), this); child.addListener("changeVisibility", () => this.fireDataEvent("changeVisibility", this.__getVisibles()), this); } else { - console.error("ToggleButtonContainer only allows ToggleButton as its children."); + console.error("CardContainer only allows CardBase as its children."); } }, @@ -43,7 +53,7 @@ qx.Class.define("osparc.dashboard.ToggleButtonContainer", { * Resets the selection so no toggle button is checked. */ resetSelection: function() { - this.getChildren().map(button => button.setValue(false)); + this.getChildren().map(button => button.setSelected(false)); this.__lastSelectedIdx = null; this.fireDataEvent("changeSelection", this.getSelection()); }, @@ -52,7 +62,7 @@ qx.Class.define("osparc.dashboard.ToggleButtonContainer", { * Returns an array that contains all buttons that are checked. */ getSelection: function() { - return this.getChildren().filter(button => button.getValue()); + return this.getChildren().filter(button => button.getSelected()); }, /** @@ -63,18 +73,18 @@ qx.Class.define("osparc.dashboard.ToggleButtonContainer", { }, /** - * Sets the given button's value to true (checks it) and unchecks all other buttons. If the given button is not present, - * every button in the container will get a false value (unchecked). - * @param {qx.ui.form.ToggleButton} child Button that will be checked + * Sets the given button's select prop to true (checks it) and unchecks all other buttons. If the given button is not present, + * every button in the container will get a unselected (unchecked). + * @param {qx.ui.form.CardBase} child Button that will be checked */ selectOne: function(child) { - this.getChildren().map(button => button.setValue(button === child)); + this.getChildren().map(button => button.setSelected(button === child)); this.setLastSelectedIndex(this.getIndex(child)); }, /** * Gets the index in the container of the given button. - * @param {qx.ui.form.ToggleButton} child Button that will be checked + * @param {qx.ui.form.CardBase} child Button that will be checked */ getIndex: function(child) { return this.getChildren().findIndex(button => button === child); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/DragDropHelpers.js b/services/static-webserver/client/source/class/osparc/dashboard/DragDropHelpers.js new file mode 100644 index 00000000000..83aed499cb4 --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/DragDropHelpers.js @@ -0,0 +1,188 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.DragDropHelpers", { + type: "static", + + statics: { + moveStudy: { + dragStart: function(event, studyItem, studyDataOrigin) { + event.addAction("move"); + event.addType("osparc-moveStudy"); + event.addData("osparc-moveStudy", { + "studyDataOrigin": studyDataOrigin, + }); + + // init drag indicator + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.getChildControl("dragged-resource").set({ + label: studyDataOrigin["name"], + icon: "@FontAwesome5Solid/file/16", + }); + dragWidget.start(); + + // make it semi transparent while being dragged + studyItem.setOpacity(0.2); + }, + + dragOver: function(event, folderItem, workspaceDestId) { + let compatible = false; + const studyDataOrigin = event.getData("osparc-moveStudy")["studyDataOrigin"]; + const workspaceIdOrigin = studyDataOrigin["workspaceId"]; + const workspaceOrigin = osparc.store.Workspaces.getInstance().getWorkspace(workspaceIdOrigin); + const workspaceDest = osparc.store.Workspaces.getInstance().getWorkspace(workspaceDestId); + // Compatibility checks: + // - Drag over "Shared Workspaces" (0) + // - No + // - My Workspace -> My Workspace (1) + // - Yes + // - My Workspace -> Shared Workspace (2) + // - Delete on Study + // - Write on dest Workspace + // - Shared Workspace -> My Workspace (3) + // - Delete on origin Workspace + // - Shared Workspace -> Shared Workspace (4) + // - Delete on origin Workspace + // - Write on dest Workspace + if (workspaceDestId === -1) { // (0) + compatible = false; + } else if (workspaceIdOrigin === null && workspaceDestId === null) { // (1) + compatible = true; + } else if (workspaceIdOrigin === null && workspaceDest) { // (2) + compatible = osparc.data.model.Study.canIDelete(studyDataOrigin["accessRights"]) && workspaceDest.getMyAccessRights()["write"]; + } else if (workspaceOrigin && workspaceDestId === null) { // (3) + compatible = workspaceOrigin.getMyAccessRights()["delete"]; + } else if (workspaceOrigin && workspaceDest) { // (4) + compatible = workspaceOrigin.getMyAccessRights()["delete"] && workspaceDest.getMyAccessRights()["write"]; + } + + if (!compatible) { + // do not allow + event.preventDefault(); + } + + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.setDropAllowed(compatible); + + folderItem.getChildControl("icon").setTextColor(compatible ? "strong-main" : "text"); + }, + + drop: function(event, folderItem, destWorkspaceId, destFolderId) { + const studyData = event.getData("osparc-moveStudy")["studyDataOrigin"]; + const studyToFolderData = { + studyData, + destWorkspaceId, + destFolderId, + }; + folderItem.getChildControl("icon").resetTextColor(); + return studyToFolderData; + }, + }, + + moveFolder: { + dragStart: function(event, folderItem, folderOrigin) { + event.addAction("move"); + event.addType("osparc-moveFolder"); + event.addData("osparc-moveFolder", { + "folderOrigin": folderOrigin, + }); + + // init drag indicator + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.getChildControl("dragged-resource").set({ + label: folderOrigin.getName(), + icon: "@FontAwesome5Solid/folder/16", + }); + dragWidget.start(); + + // make it semi transparent while being dragged + folderItem.setOpacity(0.2); + }, + + dragOver: function(event, folderItem, workspaceDestId, folderDestId) { + let compatible = false; + const folderOrigin = event.getData("osparc-moveFolder")["folderOrigin"]; + const workspaceIdOrigin = folderOrigin.getWorkspaceId(); + const workspaceOrigin = osparc.store.Workspaces.getInstance().getWorkspace(workspaceIdOrigin); + const workspaceDest = osparc.store.Workspaces.getInstance().getWorkspace(workspaceDestId); + // Compatibility checks: + // - Drag over "Shared Workspaces" (0) + // - No + // - My Workspace -> My Workspace (1) + // - Yes + // - My Workspace -> Shared Workspace (2) + // - ~~Delete on Study~~ + // - Write on dest Workspace + // - Shared Workspace -> My Workspace (3) + // - Delete on origin Workspace + // - Shared Workspace -> Shared Workspace (4) + // - Delete on origin Workspace + // - Write on dest Workspace + if (workspaceDestId === -1) { // (0) + compatible = false; + } else if (folderOrigin.getFolderId() === folderDestId) { + compatible = false; + } else if (workspaceIdOrigin === null && workspaceDestId === null) { // (1) + compatible = true; + } else if (workspaceIdOrigin === null && workspaceDest) { // (2) + compatible = workspaceDest.getMyAccessRights()["write"]; + } else if (workspaceOrigin && workspaceDestId === null) { // (3) + compatible = workspaceOrigin.getMyAccessRights()["delete"]; + } else if (workspaceOrigin && workspaceDest) { // (4) + compatible = workspaceOrigin.getMyAccessRights()["delete"] && workspaceDest.getMyAccessRights()["write"]; + } + + if (!compatible) { + // do not allow + event.preventDefault(); + } + + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.setDropAllowed(compatible); + + folderItem.getChildControl("icon").setTextColor(compatible ? "strong-main" : "text"); + }, + + drop: function(event, folderItem, destWorkspaceId, destFolderId) { + const folderOrigin = event.getData("osparc-moveFolder")["folderOrigin"]; + const folderToFolderData = { + folderId: folderOrigin.getFolderId(), + destWorkspaceId, + destFolderId, + }; + folderItem.getChildControl("icon").resetTextColor(); + return folderToFolderData; + }, + }, + + dragLeave: function(item) { + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.setDropAllowed(false); + + item.getChildControl("icon").resetTextColor(); + }, + + dragEnd: function(draggedItem) { + // bring back opacity after drag + draggedItem.setOpacity(1); + + // hide drag indicator + const dragWidget = osparc.dashboard.DragWidget.getInstance(); + dragWidget.end(); + } + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/DragWidget.js b/services/static-webserver/client/source/class/osparc/dashboard/DragWidget.js new file mode 100644 index 00000000000..64a1c188f1f --- /dev/null +++ b/services/static-webserver/client/source/class/osparc/dashboard/DragWidget.js @@ -0,0 +1,103 @@ +/* ************************************************************************ + + osparc - the simcore frontend + + https://osparc.io + + Copyright: + 2024 IT'IS Foundation, https://itis.swiss + + License: + MIT: https://opensource.org/licenses/MIT + + Authors: + * Odei Maiz (odeimaiz) + +************************************************************************ */ + +qx.Class.define("osparc.dashboard.DragWidget", { + extend: qx.ui.core.Widget, + type: "singleton", + + construct: function() { + this.base(arguments); + + this._setLayout(new qx.ui.layout.HBox(10).set({ + alignY: "middle", + })); + + this.set({ + opacity: 0.9, + padding: 10, + zIndex: 1000, + backgroundColor: "strong-main", + decorator: "rounded", + visibility: "excluded", + }); + + const root = qx.core.Init.getApplication().getRoot(); + root.add(this); + + this.initDropAllowed(); + }, + + properties: { + dropAllowed: { + check: "Boolean", + nullable: false, + init: null, + apply: "__dropAllowed", + }, + }, + + members: { + _createChildControlImpl: function(id) { + let control; + switch (id) { + case "allowed-icon": + control = new qx.ui.basic.Image(); + this._add(control); + break; + case "dragged-resource": + control = new qx.ui.basic.Atom().set({ + font: "text-14", + }); + this._add(control); + break; + } + return control || this.base(arguments, id); + }, + + __dropAllowed: function(allowed) { + this.getChildControl("allowed-icon").set({ + source: allowed ? "@FontAwesome5Solid/check/14" : "@FontAwesome5Solid/times/14", + textColor: allowed ? "text" : "danger-red", + }); + }, + + __onMouseMoveDragging: function(e) { + if (this.getContentElement()) { + // place it next to the "dragdrop-own-cursor" indicator + const domEl = this.getContentElement().getDomElement(); + domEl.style.left = `${e.pageX + 15}px`; + domEl.style.top = `${e.pageY + 5}px`; + } + }, + + start: function() { + this.show(); + document.addEventListener("mousemove", this.__onMouseMoveDragging.bind(this), false); + + const cursor = qx.ui.core.DragDropCursor.getInstance(); + cursor.setAppearance("dragdrop-no-cursor"); + }, + + end: function() { + this.exclude(); + document.removeEventListener("mousemove", this.__onMouseMoveDragging.bind(this), false); + + const cursor = qx.ui.core.DragDropCursor.getInstance(); + cursor.setAppearance("dragdrop-cursor"); + }, + } +}); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonBase.js index ff567a659cb..435e63b2129 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonBase.js @@ -16,7 +16,7 @@ ************************************************************************ */ qx.Class.define("osparc.dashboard.FolderButtonBase", { - extend: qx.ui.form.ToggleButton, + extend: qx.ui.core.Widget, implement: [qx.ui.form.IModel, osparc.filter.IFilterable], include: [qx.ui.form.MModelProperty, osparc.filter.MFilterable], type: "abstract", diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js index ac919b73579..4d11a423c34 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonItem.js @@ -33,7 +33,7 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { appearance: "pb-study" }); - this.addListener("changeValue", e => this.__itemSelected(e.getData()), this); + this.addListener("tap", this.__itemSelected, this); this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); @@ -50,6 +50,8 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { "untrashFolderRequested": "qx.event.type.Data", "deleteFolderRequested": "qx.event.type.Data", "changeContext": "qx.event.type.Data", + "studyToFolderRequested": "qx.event.type.Data", + "folderToFolderRequested": "qx.event.type.Data", }, properties: { @@ -152,6 +154,54 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { osparc.utils.Utils.setIdToWidget(this, "folderItem_" + folder.getFolderId()); this.__addMenuButton(); + + this.__attachDragHandlers(); + this.__attachDropHandlers(); + }, + + __attachDragHandlers: function() { + this.setDraggable(true); + + this.addListener("dragstart", e => { + const folderOrigin = this.getFolder(); + osparc.dashboard.DragDropHelpers.moveFolder.dragStart(e, this, folderOrigin); + }); + + this.addListener("dragend", () => { + osparc.dashboard.DragDropHelpers.dragEnd(this); + }); + }, + + __attachDropHandlers: function() { + this.setDroppable(true); + + this.addListener("dragover", e => { + const folderDest = this.getFolder(); + if (e.supportsType("osparc-moveStudy")) { + osparc.dashboard.DragDropHelpers.moveStudy.dragOver(e, this, folderDest.getWorkspaceId(), folderDest.getFolderId()); + } else if (e.supportsType("osparc-moveFolder")) { + osparc.dashboard.DragDropHelpers.moveFolder.dragOver(e, this, folderDest.getWorkspaceId(), folderDest.getFolderId()); + } + }); + + this.addListener("dragleave", () => { + osparc.dashboard.DragDropHelpers.dragLeave(this); + }); + + this.addListener("dragend", () => { + osparc.dashboard.DragDropHelpers.dragLeave(this); + }); + + this.addListener("drop", e => { + const folderDest = this.getFolder(); + if (e.supportsType("osparc-moveStudy")) { + const studyToFolderData = osparc.dashboard.DragDropHelpers.moveStudy.drop(e, this, folderDest.getWorkspaceId(), folderDest.getFolderId()); + this.fireDataEvent("studyToFolderRequested", studyToFolderData); + } else if (e.supportsType("osparc-moveFolder")) { + const folderToFolderData = osparc.dashboard.DragDropHelpers.moveFolder.drop(e, this, folderDest.getWorkspaceId(), folderDest.getFolderId()); + this.fireDataEvent("folderToFolderRequested", folderToFolderData); + } + }); }, __applyWorkspaceId: function(workspaceId) { @@ -188,9 +238,9 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { const menuButton = this.getChildControl("menu-button"); menuButton.setVisibility("visible"); - const menu = new qx.ui.menu.Menu().set({ - position: "bottom-right" - }); + const menu = new qx.ui.menu.Menu(); + menu.setPosition("bottom-right"); + osparc.utils.Utils.prettifyMenu(menu); const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext(); if ( @@ -240,13 +290,12 @@ qx.Class.define("osparc.dashboard.FolderButtonItem", { menuButton.setMenu(menu); }, - __itemSelected: function(newVal) { + __itemSelected: function() { const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext(); // do not allow selecting workspace - if (studyBrowserContext !== "trash" && newVal) { + if (studyBrowserContext !== "trash") { this.fireDataEvent("folderSelected", this.getFolderId()); } - this.setValue(false); }, __editFolder: function() { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js index 6fe4c7d9bba..42bdb7128b4 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/FolderButtonNew.js @@ -30,7 +30,7 @@ qx.Class.define("osparc.dashboard.FolderButtonNew", { appearance: "pb-new" }); - this.addListener("changeValue", e => this.__itemSelected(e.getData()), this); + this.addListener("tap", this.__itemSelected, this); this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.NEW); @@ -77,22 +77,19 @@ qx.Class.define("osparc.dashboard.FolderButtonNew", { this.getChildControl("title"); }, - __itemSelected: function(newVal) { - if (newVal) { - const newFolder = true; - const folderEditor = new osparc.editor.FolderEditor(newFolder); - const title = this.tr("New Folder"); - const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 120); - folderEditor.addListener("createFolder", () => { - const name = folderEditor.getLabel(); - this.fireDataEvent("createFolder", { - name, - }); - win.close(); + __itemSelected: function() { + const newFolder = true; + const folderEditor = new osparc.editor.FolderEditor(newFolder); + const title = this.tr("New Folder"); + const win = osparc.ui.window.Window.popUpInWindow(folderEditor, title, 300, 120); + folderEditor.addListener("createFolder", () => { + const name = folderEditor.getLabel(); + this.fireDataEvent("createFolder", { + name, }); - folderEditor.addListener("cancel", () => win.close()); - } - this.setValue(false); + win.close(); + }); + folderEditor.addListener("cancel", () => win.close()); } } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js index ad0a78c20c1..e1b7c72ff71 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonBase.js @@ -28,6 +28,8 @@ qx.Class.define("osparc.dashboard.GridButtonBase", { construct: function() { this.base(arguments); + this._setLayout(new qx.ui.layout.Canvas()); + this.set({ width: this.self().ITEM_WIDTH, height: this.self().ITEM_HEIGHT, @@ -35,8 +37,6 @@ qx.Class.define("osparc.dashboard.GridButtonBase", { allowGrowX: false }); - this._setLayout(new qx.ui.layout.Canvas()); - this.getChildControl("main-layout"); }, @@ -107,7 +107,6 @@ qx.Class.define("osparc.dashboard.GridButtonBase", { }, members: { - // overridden _createChildControlImpl: function(id) { let layout; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js index e9019262342..003648f7629 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonItem.js @@ -31,8 +31,6 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { this.base(arguments); this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); - - this.addListener("changeValue", this.__itemSelected, this); }, statics: { @@ -176,45 +174,6 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { return control || this.base(arguments, id); }, - // overridden - _applyMultiSelectionMode: function(value) { - if (value) { - const menuButton = this.getChildControl("menu-button"); - menuButton.setVisibility("excluded"); - this.__itemSelected(); - } else { - this.__showMenuOnly(); - } - }, - - __itemSelected: function() { - if (this.isItemNotClickable()) { - this.setValue(false); - return; - } - - if (this.isResourceType("study") && this.isMultiSelectionMode()) { - const selected = this.getValue(); - - const tick = this.getChildControl("tick-selected"); - tick.setVisibility(selected ? "visible" : "excluded"); - - const untick = this.getChildControl("tick-unselected"); - untick.setVisibility(selected ? "excluded" : "visible"); - } else { - this.__showMenuOnly(); - } - }, - - __showMenuOnly: function() { - const menuButton = this.getChildControl("menu-button"); - menuButton.setVisibility("visible"); - const tick = this.getChildControl("tick-selected"); - tick.setVisibility("excluded"); - const untick = this.getChildControl("tick-unselected"); - untick.setVisibility("excluded"); - }, - // overridden _applyLastChangeDate: function(value, old) { if (value && (this.isResourceType("study") || this.isResourceType("template"))) { @@ -277,7 +236,7 @@ qx.Class.define("osparc.dashboard.GridButtonItem", { const menuButton = this.getChildControl("menu-button"); if (menu) { menuButton.setMenu(menu); - menu.setPosition("top-left"); + menu.setPosition("bottom-left"); osparc.utils.Utils.prettifyMenu(menu); osparc.utils.Utils.setIdToWidget(menu, "studyItemMenuMenu"); this.evaluateMenuButtons(); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js index a10d57dcaa2..af3bf1ae666 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonLoadMore.js @@ -50,10 +50,6 @@ qx.Class.define("osparc.dashboard.GridButtonLoadMore", { this.setEnabled(!value); }, - _onToggleChange: function(e) { - this.setValue(false); - }, - _shouldApplyFilter: function() { return false; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonNew.js index 4a2a3577e31..3cb8a8c92b7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonNew.js @@ -67,10 +67,6 @@ qx.Class.define("osparc.dashboard.GridButtonNew", { }, members: { - _onToggleChange: function(e) { - this.setValue(false); - }, - _shouldApplyFilter: function(data) { return false; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonPlaceholder.js b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonPlaceholder.js index 89f9c94270a..b6eb9906ef7 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GridButtonPlaceholder.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GridButtonPlaceholder.js @@ -23,9 +23,6 @@ qx.Class.define("osparc.dashboard.GridButtonPlaceholder", { this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.PLACEHOLDER); - // make unselectable - this.addListener("changeValue", () => this.setValue(false), this); - this.set({ cursor: "not-allowed" }); @@ -122,10 +119,6 @@ qx.Class.define("osparc.dashboard.GridButtonPlaceholder", { return true; }, - _onToggleChange: function() { - this.setValue(false); - }, - _shouldApplyFilter: function(data) { if (data.text) { const checks = [ diff --git a/services/static-webserver/client/source/class/osparc/dashboard/GroupedToggleButtonContainer.js b/services/static-webserver/client/source/class/osparc/dashboard/GroupedCardContainer.js similarity index 95% rename from services/static-webserver/client/source/class/osparc/dashboard/GroupedToggleButtonContainer.js rename to services/static-webserver/client/source/class/osparc/dashboard/GroupedCardContainer.js index d5dc5505d09..2223517302c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/GroupedToggleButtonContainer.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/GroupedCardContainer.js @@ -15,7 +15,7 @@ ************************************************************************ */ -qx.Class.define("osparc.dashboard.GroupedToggleButtonContainer", { +qx.Class.define("osparc.dashboard.GroupedCardContainer", { extend: qx.ui.core.Widget, construct: function() { @@ -118,7 +118,7 @@ qx.Class.define("osparc.dashboard.GroupedToggleButtonContainer", { const expanded = this.isExpanded(); const showAllBtn = this.__showAllButton; if (expanded) { - contentContainer = new osparc.dashboard.ToggleButtonContainer(); + contentContainer = new osparc.dashboard.CardContainer(); showAllBtn.show(); } else { const spacing = osparc.dashboard.GridButtonBase.SPACING; @@ -176,7 +176,7 @@ qx.Class.define("osparc.dashboard.GroupedToggleButtonContainer", { // overridden add: function(child, idx) { - if (child instanceof qx.ui.form.ToggleButton) { + if (osparc.dashboard.CardContainer.isValidCard(child)) { const container = this.getContentContainer(); if (osparc.dashboard.ResourceContainerManager.cardExists(container, child)) { return; @@ -189,7 +189,7 @@ qx.Class.define("osparc.dashboard.GroupedToggleButtonContainer", { } this.__childVisibilityChanged(); } else { - console.error("ToggleButtonContainer only allows ToggleButton as its children."); + console.error("CardContainer only allows CardBase as its children."); } }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js index 86decb00157..d99d33f6608 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonBase.js @@ -27,16 +27,17 @@ qx.Class.define("osparc.dashboard.ListButtonBase", { construct: function() { this.base(arguments); - this.set({ - minHeight: osparc.dashboard.ListButtonBase.ITEM_HEIGHT, - allowGrowX: true - }); const layout = new qx.ui.layout.Grid(); layout.setSpacing(10); layout.setColumnFlex(osparc.dashboard.ListButtonBase.POS.SPACER, 1); this._setLayout(layout); + this.set({ + minHeight: osparc.dashboard.ListButtonBase.ITEM_HEIGHT, + allowGrowX: true + }); + this.getChildControl("spacer"); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js index 9c433550185..5a80947d803 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonItem.js @@ -28,8 +28,6 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { this.base(arguments); this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); - - this.addListener("changeValue", this.__itemSelected, this); }, statics: { @@ -262,48 +260,16 @@ qx.Class.define("osparc.dashboard.ListButtonItem", { }); }, - // overridden - _applyMultiSelectionMode: function(value) { - if (value) { - const menuButton = this.getChildControl("menu-button"); - menuButton.setVisibility("excluded"); - this.__itemSelected(); - } else { - this.__showMenuOnly(); - } - }, - - __itemSelected: function() { - if (this.isItemNotClickable()) { - this.setValue(false); - return; - } - - if (this.isResourceType("study") && this.isMultiSelectionMode()) { - const selected = this.getValue(); - - const tick = this.getChildControl("tick-selected"); - tick.setVisibility(selected ? "visible" : "excluded"); - - const untick = this.getChildControl("tick-unselected"); - untick.setVisibility(selected ? "excluded" : "visible"); - } else { - this.__showMenuOnly(); - } - }, - - __showMenuOnly: function() { - const menu = this.getChildControl("menu-button"); - this.getChildControl("menu-selection-stack").setSelection([menu]); - }, - - _applyMenu: function(value, old) { + _applyMenu: function(menu, old) { const menuButton = this.getChildControl("menu-button"); - if (value) { - menuButton.setMenu(value); - osparc.utils.Utils.setIdToWidget(value, "studyItemMenuMenu"); + if (menu) { + menuButton.setMenu(menu); + menu.setPosition("bottom-left"); + osparc.utils.Utils.prettifyMenu(menu); + osparc.utils.Utils.setIdToWidget(menu, "studyItemMenuMenu"); + this.evaluateMenuButtons(); } - menuButton.setVisibility(value ? "visible" : "excluded"); + menuButton.setVisibility(menu ? "visible" : "excluded"); } } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js index cbf818c8cdc..1f0fad3e4a6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonLoadMore.js @@ -49,10 +49,6 @@ qx.Class.define("osparc.dashboard.ListButtonLoadMore", { this.setEnabled(!value); }, - _onToggleChange: function(e) { - this.setValue(false); - }, - _shouldApplyFilter: function() { return false; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonNew.js index d9bb0679f46..7ae28a96cf4 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonNew.js @@ -51,10 +51,6 @@ qx.Class.define("osparc.dashboard.ListButtonNew", { }, members: { - _onToggleChange: function(e) { - this.setValue(false); - }, - _shouldApplyFilter: function(data) { return false; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonPlaceholder.js b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonPlaceholder.js index 7074ded3194..d813261ef3c 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ListButtonPlaceholder.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ListButtonPlaceholder.js @@ -23,9 +23,6 @@ qx.Class.define("osparc.dashboard.ListButtonPlaceholder", { this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.PLACEHOLDER); - // make unselectable - this.addListener("changeValue", () => this.setValue(false), this); - this.__layout = this.getChildControl("progress-layout") this.set({ appearance: "pb-new", @@ -108,10 +105,6 @@ qx.Class.define("osparc.dashboard.ListButtonPlaceholder", { return true; }, - _onToggleChange: function() { - this.setValue(false); - }, - _shouldApplyFilter: function(data) { if (data.text) { const checks = [ diff --git a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js index ecd26def377..aefa81ef810 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/MoveResourceTo.js @@ -73,7 +73,7 @@ qx.Class.define("osparc.dashboard.MoveResourceTo", { switch (id) { case "current-location": { control = new qx.ui.container.Composite(new qx.ui.layout.VBox(5)); - const intro = new qx.ui.basic.Label(this.tr("Current location")); + const intro = new qx.ui.basic.Label(this.tr("Current location:")); control.add(intro); const workspace = osparc.store.Workspaces.getInstance().getWorkspace(this.__currentWorkspaceId); const workspaceText = workspace ? workspace.getName() : "My Workspace"; diff --git a/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js b/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js index 7c01ff5c74d..0e0d92b61b6 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/NewStudies.js @@ -27,7 +27,7 @@ qx.Class.define("osparc.dashboard.NewStudies", { this._setLayout(new qx.ui.layout.VBox(10)); - const flatList = this.__flatList = new osparc.dashboard.ToggleButtonContainer(); + const flatList = this.__flatList = new osparc.dashboard.CardContainer(); [ "changeSelection", "changeVisibility" @@ -86,7 +86,7 @@ qx.Class.define("osparc.dashboard.NewStudies", { this._add(groupContainer); }); } else { - const flatList = this.__flatList = new osparc.dashboard.ToggleButtonContainer(); + const flatList = this.__flatList = new osparc.dashboard.CardContainer(); osparc.utils.Utils.setIdToWidget(flatList, listId); [ "changeSelection", @@ -138,7 +138,7 @@ qx.Class.define("osparc.dashboard.NewStudies", { }, __createGroupContainer: function(groupId, headerLabel, headerColor = "text") { - const groupContainer = new osparc.dashboard.GroupedToggleButtonContainer().set({ + const groupContainer = new osparc.dashboard.GroupedCardContainer().set({ groupId: groupId.toString(), headerLabel, headerIcon: "", diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js index c007ca05f7e..8c3cfd23637 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceBrowserBase.js @@ -279,6 +279,8 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { resourcesContainer.addListener("trashFolderRequested", e => this._trashFolderRequested(e.getData())); resourcesContainer.addListener("untrashFolderRequested", e => this._untrashFolderRequested(e.getData())); resourcesContainer.addListener("deleteFolderRequested", e => this._deleteFolderRequested(e.getData())); + resourcesContainer.addListener("studyToFolderRequested", e => this._studyToFolderRequested(e.getData())); + resourcesContainer.addListener("folderToFolderRequested", e => this._folderToFolderRequested(e.getData())); resourcesContainer.addListener("folderSelected", e => { const folderId = e.getData(); this._folderSelected(folderId); @@ -524,6 +526,14 @@ qx.Class.define("osparc.dashboard.ResourceBrowserBase", { throw new Error("Abstract method called!"); }, + _studyToFolderRequested: function(studyId) { + throw new Error("Abstract method called!"); + }, + + _folderToFolderRequested: function(folderId) { + throw new Error("Abstract method called!"); + }, + _workspaceSelected: function(workspaceId) { throw new Error("Abstract method called!"); }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js index 679c2b45cf1..55ac1f85697 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ResourceContainerManager.js @@ -33,10 +33,10 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.__groupedContainersList = []; if (resourceType === "study") { - const workspacesContainer = this.__workspacesContainer = new osparc.dashboard.ToggleButtonContainer(); + const workspacesContainer = this.__workspacesContainer = new osparc.dashboard.CardContainer(); this._add(workspacesContainer); - const foldersContainer = this.__foldersContainer = new osparc.dashboard.ToggleButtonContainer(); + const foldersContainer = this.__foldersContainer = new osparc.dashboard.CardContainer(); this._add(foldersContainer); } @@ -83,6 +83,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "untrashWorkspaceRequested": "qx.event.type.Data", "deleteWorkspaceRequested": "qx.event.type.Data", "changeContext": "qx.event.type.Data", + "studyToFolderRequested": "qx.event.type.Data", + "folderToFolderRequested": "qx.event.type.Data", }, statics: { @@ -118,7 +120,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { __groupedContainers: null, addNonResourceCard: function(card) { - if (card instanceof qx.ui.form.ToggleButton) { + if (osparc.dashboard.CardContainer.isValidCard(card)) { if (this.getGroupBy()) { // it will always go to the no-group group const noGroupContainer = this.__getGroupContainer("no-group"); @@ -129,12 +131,12 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.self().sortListByPriority(this.__nonGroupedContainer); } } else { - console.error("ToggleButtonContainer only allows ToggleButton as its children."); + console.error("CardContainer only allows CardBase as its children."); } }, removeNonResourceCard: function(card) { - if (card instanceof qx.ui.form.ToggleButton) { + if (osparc.dashboard.CardContainer.isValidCard(card)) { if (this.getGroupBy()) { const noGroupContainer = this.__getGroupContainer("no-group"); if (noGroupContainer.getContentContainer().getChildren().indexOf(card) > -1) { @@ -144,7 +146,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { this.__nonGroupedContainer.remove(card); } } else { - console.error("ToggleButtonContainer only allows ToggleButton as its children."); + console.error("CardContainer only allows CardBase as its children."); } }, @@ -161,7 +163,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { }, __createGroupContainer: function(groupId, headerLabel, headerColor = "text") { - const groupContainer = new osparc.dashboard.GroupedToggleButtonContainer().set({ + const groupContainer = new osparc.dashboard.GroupedCardContainer().set({ groupId: groupId.toString(), headerLabel, headerIcon: "", @@ -317,7 +319,7 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { }, __createFlatList: function() { - const flatList = new osparc.dashboard.ToggleButtonContainer(); + const flatList = new osparc.dashboard.CardContainer(); const setContainerSpacing = () => { const spacing = this.getMode() === "grid" ? osparc.dashboard.GridButtonBase.SPACING : osparc.dashboard.ListButtonBase.SPACING; flatList.getLayout().set({ @@ -429,6 +431,8 @@ qx.Class.define("osparc.dashboard.ResourceContainerManager", { "untrashFolderRequested", "deleteFolderRequested", "changeContext", + "studyToFolderRequested", + "folderToFolderRequested", ].forEach(eName => card.addListener(eName, e => this.fireDataEvent(eName, e.getData()))); return card; }, diff --git a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js index 5fbaa4ebaf7..7ae65ff0bd1 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/ServiceBrowser.js @@ -86,7 +86,7 @@ qx.Class.define("osparc.dashboard.ServiceBrowser", { const cards = this._resourcesContainer.reloadCards("services"); cards.forEach(card => { card.setMultiSelectionMode(this.getMultiSelection()); - card.addListener("execute", () => this.__itemClicked(card), this); + card.addListener("tap", () => this.__itemClicked(card), this); this._populateCardMenu(card); }); osparc.filter.UIFilterController.dispatch("searchBarFilter"); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js index 0a25257f247..081d63a2a96 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/StudyBrowser.js @@ -48,7 +48,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, events: { - "publishTemplate": "qx.event.type.Data" + "publishTemplate": "qx.event.type.Data", }, properties: { @@ -100,6 +100,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __foldersList: null, __loadingFolders: null, __loadingWorkspaces: null, + __dragWidget: null, // overridden initResources: function() { @@ -564,6 +565,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return win; }, + __doMoveFolder: function(folderId, destWorkspaceId, destFolderId) { + osparc.store.Folders.getInstance().moveFolderToWorkspace(folderId, destWorkspaceId) // first move to workspace + .then(() => osparc.store.Folders.getInstance().moveFolderToFolder(folderId, destFolderId)) // then move to folder + .then(() => this.__reloadFolders()) + .catch(err => console.error(err)); + }, + _moveFolderToRequested: function(folderId) { const currentWorkspaceId = this.getCurrentWorkspaceId(); const currentFolderId = this.getCurrentFolderId(); @@ -575,19 +583,13 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const data = e.getData(); const destWorkspaceId = data["workspaceId"]; const destFolderId = data["folderId"]; - const moveFolder = () => { - osparc.store.Folders.getInstance().moveFolderToWorkspace(folderId, destWorkspaceId) // first move to workspace - .then(() => osparc.store.Folders.getInstance().moveFolderToFolder(folderId, destFolderId)) // then move to folder - .then(() => this.__reloadFolders()) - .catch(err => console.error(err)); - } if (destWorkspaceId === currentWorkspaceId) { - moveFolder(); + this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); } else { const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); confirmationWin.addListener("close", () => { if (confirmationWin.getConfirmed()) { - moveFolder(); + this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); } }, this); } @@ -595,6 +597,15 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { moveFolderTo.addListener("cancel", () => win.close()); }, + _folderToFolderRequested: function(data) { + const { + folderId, + destWorkspaceId, + destFolderId, + } = data; + this.__doMoveFolder(folderId, destWorkspaceId, destFolderId); + }, + _trashFolderRequested: function(folderId) { osparc.store.Folders.getInstance().trashFolder(folderId, this.getCurrentWorkspaceId()) .then(() => { @@ -638,34 +649,48 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { __configureStudyCards: function(cards) { cards.forEach(card => { card.setMultiSelectionMode(this.getMultiSelection()); - card.addListener("tap", e => { - if (card.isItemNotClickable()) { - card.setValue(false); - } else { - this.__itemClicked(card, e.getNativeEvent().shiftKey); - } - }, this); + card.addListener("tap", e => this.__studyCardClicked(card, e.getNativeEvent().shiftKey), this); this._populateCardMenu(card); + + this.__attachDragHandlers(card); }); }, - __itemClicked: function(item, isShiftPressed) { - const studiesCont = this._resourcesContainer.getFlatList(); + __attachDragHandlers: function(card) { + card.setDraggable(true); - if (isShiftPressed) { - const lastIdx = studiesCont.getLastSelectedIndex(); - const currentIdx = studiesCont.getIndex(item); - const minMax = [Math.min(lastIdx, currentIdx), Math.max(lastIdx, currentIdx)]; - for (let i=minMax[0]; i<=minMax[1]; i++) { - const card = studiesCont.getChildren()[i]; - if (card.isVisible()) { - card.setValue(true); - } - } + card.addListener("dragstart", e => { + const studyDataOrigin = card.getResourceData(); + osparc.dashboard.DragDropHelpers.moveStudy.dragStart(e, card, studyDataOrigin); + }); + + card.addListener("dragend", () => { + osparc.dashboard.DragDropHelpers.dragEnd(card); + }); + }, + + __studyCardClicked: function(item, isShiftPressed) { + if (item.isItemNotClickable()) { + item.setSelected(false); + return; } - studiesCont.setLastSelectedIndex(studiesCont.getIndex(item)); - if (!item.isMultiSelectionMode()) { + if (item.isMultiSelectionMode()) { + item.setSelected(!item.getSelected()); + const studiesCont = this._resourcesContainer.getFlatList(); + if (isShiftPressed) { + const lastIdx = studiesCont.getLastSelectedIndex(); + const currentIdx = studiesCont.getIndex(item); + const minMax = [Math.min(lastIdx, currentIdx), Math.max(lastIdx, currentIdx)]; + for (let i=minMax[0]; i<=minMax[1]; i++) { + const card = studiesCont.getChildren()[i]; + if (card.isVisible()) { + card.setSelected(true); + } + } + } + studiesCont.setLastSelectedIndex(studiesCont.getIndex(item)); + } else { const studyData = this.__getStudyData(item.getUuid(), false); this._openResourceDetails(studyData); this.resetSelection(); @@ -860,7 +885,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { newStudyBtn.setCardKey("new-study"); newStudyBtn.subscribeToFilterGroup("searchBarFilter"); osparc.utils.Utils.setIdToWidget(newStudyBtn, "newStudyBtn"); - newStudyBtn.addListener("execute", () => this.__newStudyBtnClicked(newStudyBtn)); + newStudyBtn.addListener("tap", () => this.__newStudyBtnClicked(newStudyBtn)); this._resourcesContainer.addNonResourceCard(newStudyBtn); }, @@ -880,8 +905,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (product in newStudiesData) { newStudyBtn.setEnabled(true); - newStudyBtn.addListener("execute", () => { - newStudyBtn.setValue(false); + newStudyBtn.addListener("tap", () => { osparc.data.Resources.get("templates") .then(templates => { if (templates) { @@ -930,7 +954,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const newStudyFromServiceButton = (mode === "grid") ? new osparc.dashboard.GridButtonNew(title, desc) : new osparc.dashboard.ListButtonNew(title, desc); newStudyFromServiceButton.setCardKey("new-"+key); osparc.utils.Utils.setIdToWidget(newStudyFromServiceButton, newButtonInfo.idToWidget); - newStudyFromServiceButton.addListener("execute", () => this.__newStudyFromServiceBtnClicked(newStudyFromServiceButton, latestMetadata["key"], latestMetadata["version"], newButtonInfo.newStudyLabel)); + newStudyFromServiceButton.addListener("tap", () => this.__newStudyFromServiceBtnClicked(newStudyFromServiceButton, latestMetadata["key"], latestMetadata["version"], newButtonInfo.newStudyLabel)); this._resourcesContainer.addNonResourceCard(newStudyFromServiceButton); }) } @@ -987,6 +1011,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._addResourceFilter(); this.__connectContexts(); + this.__connectDropHandlers(); this.__addNewStudyButtons(); @@ -1134,6 +1159,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { this._resourceFilter.contextChanged(context, workspaceId, folderId); }, + __connectDropHandlers: function() { + const workspacesAndFoldersTree = this._resourceFilter.getWorkspacesAndFoldersTree(); + workspacesAndFoldersTree.addListener("studyToFolderRequested", e => { + this._studyToFolderRequested(e.getData()); + }); + workspacesAndFoldersTree.addListener("folderToFolderRequested", e => { + this._folderToFolderRequested(e.getData()); + }); + }, + __addSortByButton: function() { const sortByButton = new osparc.dashboard.SortedByMenuButton(); sortByButton.set({ @@ -1153,8 +1188,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const loadMoreBtn = this._loadingResourcesBtn = (mode === "grid") ? new osparc.dashboard.GridButtonLoadMore() : new osparc.dashboard.ListButtonLoadMore(); loadMoreBtn.setCardKey("load-more"); osparc.utils.Utils.setIdToWidget(loadMoreBtn, "studiesLoading"); - loadMoreBtn.addListener("execute", () => { - loadMoreBtn.setValue(false); + loadMoreBtn.addListener("tap", () => { this._moreResourcesRequired(); }); return loadMoreBtn; @@ -1209,13 +1243,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const selection = this._resourcesContainer.getSelection(); selection.forEach(button => { const studyData = button.getResourceData(); - this.__moveStudyToWorkspace(studyData, destWorkspaceId) // first move to workspace - .then(() => this.__moveStudyToFolder(studyData, destFolderId)) // then move to folder - .then(() => this.__removeFromStudyList(studyData["uuid"])) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }); + this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); }); this.resetSelection(); this.setMultiSelection(false); @@ -1324,7 +1352,7 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { if (osparc.dashboard.ResourceBrowserBase.isCardButtonItem(studyItem)) { studyItem.setMultiSelectionMode(value); if (value === false) { - studyItem.setValue(false); + studyItem.setSelected(false); } } }); @@ -1347,7 +1375,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __newStudyBtnClicked: function(button) { - button.setValue(false); const minStudyData = osparc.data.model.Study.createMinStudyObject(); const existingNames = this._resourcesList.map(study => study["name"]); const title = osparc.utils.Utils.getUniqueName(minStudyData.name, existingNames); @@ -1388,7 +1415,6 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { }, __newStudyFromServiceBtnClicked: function(button, key, version, newStudyLabel) { - button.setValue(false); this._showLoadingPage(this.tr("Creating ") + osparc.product.Utils.getStudyAlias()); const contextProps = { workspaceId: this.getCurrentWorkspaceId(), @@ -1616,6 +1642,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { return studyBillingSettingsButton; }, + __doMoveStudy: function(studyData, destWorkspaceId, destFolderId) { + this.__moveStudyToWorkspace(studyData, destWorkspaceId) // first move to workspace + .then(() => this.__moveStudyToFolder(studyData, destFolderId)) // then move to folder + .then(() => this.__removeFromStudyList(studyData["uuid"])) + .catch(err => { + console.error(err); + osparc.FlashMessenger.logAs(err.message, "ERROR"); + }); + }, + __getMoveStudyToMenuButton: function(studyData) { const moveToButton = new qx.ui.menu.Button(this.tr("Move to..."), "@FontAwesome5Solid/folder/12"); moveToButton["moveToButton"] = true; @@ -1630,22 +1666,14 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { const data = e.getData(); const destWorkspaceId = data["workspaceId"]; const destFolderId = data["folderId"]; - const moveStudy = () => { - this.__moveStudyToWorkspace(studyData, destWorkspaceId) // first move to workspace - .then(() => this.__moveStudyToFolder(studyData, destFolderId)) // then move to folder - .then(() => this.__removeFromStudyList(studyData["uuid"])) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }); - }; + if (destWorkspaceId === currentWorkspaceId) { - moveStudy(); + this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); } else { const confirmationWin = this.__showMoveToWorkspaceWarningMessage(); confirmationWin.addListener("close", () => { if (confirmationWin.getConfirmed()) { - moveStudy(); + this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); } }, this); } @@ -1686,11 +1714,16 @@ qx.Class.define("osparc.dashboard.StudyBrowser", { } }; return osparc.data.Resources.fetch("studies", "moveToFolder", params) - .then(() => studyData["folderId"] = destFolderId) - .catch(err => { - console.error(err); - osparc.FlashMessenger.logAs(err.message, "ERROR"); - }); + .then(() => studyData["folderId"] = destFolderId); + }, + + _studyToFolderRequested: function(data) { + const { + studyData, + destWorkspaceId, + destFolderId, + } = data; + this.__doMoveStudy(studyData, destWorkspaceId, destFolderId); }, __getDuplicateMenuButton: function(studyData) { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js index 0b6fc8ccd26..fab2dc1eb94 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/TemplateBrowser.js @@ -125,7 +125,6 @@ qx.Class.define("osparc.dashboard.TemplateBrowser", { __itemClicked: function(card) { if (!card.getBlocked()) { - card.setValue(false); const templateData = this.__getTemplateData(card.getUuid()); this._openResourceDetails(templateData); } diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js index c0c93cc9508..a6fb451fc2d 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonBase.js @@ -16,7 +16,7 @@ ************************************************************************ */ qx.Class.define("osparc.dashboard.WorkspaceButtonBase", { - extend: qx.ui.form.ToggleButton, + extend: qx.ui.core.Widget, implement: [qx.ui.form.IModel, osparc.filter.IFilterable], include: [qx.ui.form.MModelProperty, osparc.filter.MFilterable], type: "abstract", @@ -24,14 +24,14 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonBase", { construct: function() { this.base(arguments); + this._setLayout(new qx.ui.layout.Canvas()); + this.set({ width: this.self().ITEM_WIDTH, height: this.self().ITEM_HEIGHT, padding: 0 }); - this._setLayout(new qx.ui.layout.Canvas()); - this.getChildControl("main-layout"); [ @@ -51,6 +51,13 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonBase", { nullable: true }, + icon: { + check: "String", + init: null, + nullable: true, + apply: "_applyIcon", + }, + resourceType: { check: ["workspace"], init: "workspace", diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js index eb777ca5dd7..91ab3a26233 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonItem.js @@ -33,7 +33,7 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { appearance: "pb-listitem" }); - this.addListener("changeValue", e => this.__itemSelected(e.getData()), this); + this.addListener("tap", this.__itemSelected, this); this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.ITEM); @@ -181,9 +181,9 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { const menuButton = this.getChildControl("menu-button"); menuButton.setVisibility("visible"); - const menu = new qx.ui.menu.Menu().set({ - position: "bottom-right" - }); + const menu = new qx.ui.menu.Menu(); + menu.setPosition("bottom-right"); + osparc.utils.Utils.prettifyMenu(menu); const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext(); if ( @@ -253,13 +253,12 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonItem", { }) }, - __itemSelected: function(newVal) { + __itemSelected: function() { const studyBrowserContext = osparc.store.Store.getInstance().getStudyBrowserContext(); // do not allow selecting workspace - if (studyBrowserContext !== "trash" && newVal) { + if (studyBrowserContext !== "trash") { this.fireDataEvent("workspaceSelected", this.getWorkspaceId()); } - this.setValue(false); }, __openShareWith: function() { diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js index dd65702503b..aa8425858a8 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspaceButtonNew.js @@ -30,7 +30,7 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonNew", { appearance: "pb-new" }); - this.addListener("changeValue", e => this.__itemSelected(e.getData()), this); + this.addListener("tap", this.__itemSelected, this); this.setPriority(osparc.dashboard.CardBase.CARD_PRIORITY.NEW); @@ -54,25 +54,22 @@ qx.Class.define("osparc.dashboard.WorkspaceButtonNew", { }, members: { - __itemSelected: function(newVal) { - if (newVal) { - const workspaceEditor = new osparc.editor.WorkspaceEditor(); - const title = this.tr("New Workspace"); - const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 500, 500).set({ - modal: true, - clickAwayClose: false, - }); - workspaceEditor.addListener("workspaceCreated", () => this.fireEvent("workspaceCreated")); - workspaceEditor.addListener("workspaceDeleted", () => this.fireEvent("workspaceDeleted")); - workspaceEditor.addListener("workspaceUpdated", () => { - win.close(); - this.fireEvent("workspaceUpdated"); - }, this); - workspaceEditor.addListener("updateAccessRights", () => this.fireEvent("workspaceUpdated")); - win.getChildControl("close-button").addListener("tap", () => workspaceEditor.cancel()); - workspaceEditor.addListener("cancel", () => win.close()); - } - this.setValue(false); + __itemSelected: function() { + const workspaceEditor = new osparc.editor.WorkspaceEditor(); + const title = this.tr("New Workspace"); + const win = osparc.ui.window.Window.popUpInWindow(workspaceEditor, title, 500, 500).set({ + modal: true, + clickAwayClose: false, + }); + workspaceEditor.addListener("workspaceCreated", () => this.fireEvent("workspaceCreated")); + workspaceEditor.addListener("workspaceDeleted", () => this.fireEvent("workspaceDeleted")); + workspaceEditor.addListener("workspaceUpdated", () => { + win.close(); + this.fireEvent("workspaceUpdated"); + }, this); + workspaceEditor.addListener("updateAccessRights", () => this.fireEvent("workspaceUpdated")); + win.getChildControl("close-button").addListener("tap", () => workspaceEditor.cancel()); + workspaceEditor.addListener("cancel", () => win.close()); } } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js index 01cea4d878c..604d5e2e7b0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTree.js @@ -85,6 +85,8 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { events: { "openChanged": "qx.event.type.Event", "locationChanged": "qx.event.type.Data", + "studyToFolderRequested": "qx.event.type.Data", + "folderToFolderRequested": "qx.event.type.Data", }, properties: { @@ -133,7 +135,13 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTree", { item.addListener("changeModel", e => { const model = e.getData(); osparc.utils.Utils.setIdToWidget(item, `workspacesAndFoldersTreeItem_${model.getWorkspaceId()}_${model.getFolderId()}`); - }) + }); + [ + "studyToFolderRequested", + "folderToFolderRequested", + ].forEach(ev => { + item.addListener(ev, e => this.fireDataEvent(ev, e.getData())); + }); } }); diff --git a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js index 75f120a86c5..05a4e44a9c0 100644 --- a/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js +++ b/services/static-webserver/client/source/class/osparc/dashboard/WorkspacesAndFoldersTreeItem.js @@ -30,6 +30,14 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTreeItem", { this.setNotHoveredStyle(); this.__attachEventHandlers(); + + this.__attachDragHandlers(); + this.__attachDropHandlers(); + }, + + events: { + "studyToFolderRequested": "qx.event.type.Data", + "folderToFolderRequested": "qx.event.type.Data", }, members: { @@ -48,6 +56,77 @@ qx.Class.define("osparc.dashboard.WorkspacesAndFoldersTreeItem", { setNotHoveredStyle: function() { osparc.utils.Utils.hideBorder(this); - } + }, + + __getFolder: function() { + const folderId = this.getModel().getFolderId(); + if (folderId === null) { + return null; + } + return osparc.store.Folders.getInstance().getFolder(folderId); + }, + + __attachDragHandlers: function() { + this.setDraggable(true); + + this.addListener("dragstart", e => { + const folderOrigin = this.__getFolder(); + // only folders can be dragged + if (folderOrigin == null) { + e.preventDefault(); + return; + } + osparc.dashboard.DragDropHelpers.moveFolder.dragStart(e, this, folderOrigin); + }); + + this.addListener("dragend", () => { + osparc.dashboard.DragDropHelpers.dragEnd(this); + }); + }, + + __attachDropHandlers: function() { + this.setDroppable(true); + + let draggingOver = false; + this.addListener("dragover", e => { + const workspaceDestId = this.getModel().getWorkspaceId(); + const folderDestId = this.getModel().getFolderId(); + if (e.supportsType("osparc-moveStudy")) { + osparc.dashboard.DragDropHelpers.moveStudy.dragOver(e, this, workspaceDestId, folderDestId); + } else if (e.supportsType("osparc-moveFolder")) { + osparc.dashboard.DragDropHelpers.moveFolder.dragOver(e, this, workspaceDestId, folderDestId); + } + + draggingOver = true; + setTimeout(() => { + if (draggingOver) { + this.setOpen(true); + draggingOver = false; + } + }, 1000); + }); + + this.addListener("dragleave", () => { + osparc.dashboard.DragDropHelpers.dragLeave(this); + draggingOver = false; + }); + this.addListener("dragend", () => { + osparc.dashboard.DragDropHelpers.dragLeave(this); + draggingOver = false; + }); + + this.addListener("drop", e => { + const workspaceDestId = this.getModel().getWorkspaceId(); + const folderDestId = this.getModel().getFolderId(); + if (e.supportsType("osparc-moveStudy")) { + const studyToFolderData = osparc.dashboard.DragDropHelpers.moveStudy.drop(e, this, workspaceDestId, folderDestId); + this.fireDataEvent("studyToFolderRequested", studyToFolderData); + } else if (e.supportsType("osparc-moveFolder")) { + const folderToFolderData = osparc.dashboard.DragDropHelpers.moveFolder.drop(e, this, workspaceDestId, folderDestId); + this.fireDataEvent("folderToFolderRequested", folderToFolderData); + } + draggingOver = false; + }); + }, }, }); diff --git a/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js b/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js index 40a3e5b5918..1d19d05f390 100644 --- a/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js +++ b/services/static-webserver/client/source/class/osparc/desktop/account/MyAccount.js @@ -64,17 +64,24 @@ qx.Class.define("osparc.desktop.account.MyAccount", { authData.bind("username", usernameLabel, "value"); layout.add(usernameLabel); - const name = new qx.ui.basic.Label().set({ + const nameLabel = new qx.ui.basic.Label().set({ font: "text-13", alignX: "center" }); - layout.add(name); - authData.bind("firstName", name, "value", { - converter: firstName => firstName + " " + authData.getLastName() - }); - authData.bind("lastName", name, "value", { - converter: lastName => authData.getFirstName() + " " + lastName - }); + layout.add(nameLabel); + const updateName = () => { + let name = ""; + if (authData.getFirstName()) { + name += authData.getFirstName(); + } + if (authData.getLastName()) { + name += " " + authData.getLastName(); + } + nameLabel.setValue(name); + } + updateName(); + authData.addListener("changeFirstName", updateName); + authData.addListener("changeLastName", updateName); if (authData.getRole() !== "user") { const role = authData.getFriendlyRole(); diff --git a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js index 15eec413914..35837fff2c7 100644 --- a/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js +++ b/services/static-webserver/client/source/class/osparc/file/FileLabelWithActions.js @@ -198,7 +198,7 @@ qx.Class.define("osparc.file.FileLabelWithActions", { request .then(data => { this.fireDataEvent("fileDeleted", data); - osparc.FlashMessenger.getInstance().logAs(this.tr("File successfully deleted"), "ERROR"); + osparc.FlashMessenger.getInstance().logAs(this.tr("File successfully deleted"), "INFO"); }); } } diff --git a/services/static-webserver/client/source/class/osparc/pricing/UnitEditor.js b/services/static-webserver/client/source/class/osparc/pricing/UnitEditor.js index 26469666570..38f9022172e 100644 --- a/services/static-webserver/client/source/class/osparc/pricing/UnitEditor.js +++ b/services/static-webserver/client/source/class/osparc/pricing/UnitEditor.js @@ -36,7 +36,6 @@ qx.Class.define("osparc.pricing.UnitEditor", { const manager = this.__validator = new qx.ui.form.validation.Manager(); unitName.setRequired(true); costPerUnit.setRequired(true); - specificInfo.setRequired(true); unitExtraInfoCPU.setRequired(true); unitExtraInfoRAM.setRequired(true); unitExtraInfoVRAM.setRequired(true); @@ -114,8 +113,8 @@ qx.Class.define("osparc.pricing.UnitEditor", { specificInfo: { check: "String", - init: "t2.medium", - nullable: false, + init: null, + nullable: true, event: "changeSpecificInfo" }, @@ -307,7 +306,11 @@ qx.Class.define("osparc.pricing.UnitEditor", { const unitName = this.getUnitName(); const costPerUnit = this.getCostPerUnit(); const comment = this.getComment(); + const awsEc2Instances = []; const specificInfo = this.getSpecificInfo(); + if (specificInfo) { + awsEc2Instances.push(specificInfo); + } const extraInfo = {}; extraInfo["CPU"] = this.getUnitExtraInfoCPU(); extraInfo["RAM"] = this.getUnitExtraInfoRAM(); @@ -323,7 +326,7 @@ qx.Class.define("osparc.pricing.UnitEditor", { "costPerUnit": costPerUnit, "comment": comment, "specificInfo": { - "aws_ec2_instances": [specificInfo] + "aws_ec2_instances": awsEc2Instances }, "unitExtraInfo": extraInfo, "default": isDefault diff --git a/services/static-webserver/client/source/class/osparc/service/ServiceList.js b/services/static-webserver/client/source/class/osparc/service/ServiceList.js index 06ca9bca1e7..72d1164f6dd 100644 --- a/services/static-webserver/client/source/class/osparc/service/ServiceList.js +++ b/services/static-webserver/client/source/class/osparc/service/ServiceList.js @@ -36,7 +36,7 @@ qx.Class.define("osparc.service.ServiceList", { }, events: { - "changeValue": "qx.event.type.Data", + "changeSelected": "qx.event.type.Data", "serviceAdd": "qx.event.type.Data" }, @@ -53,33 +53,26 @@ qx.Class.define("osparc.service.ServiceList", { }, members: { - __buttonGroup: null, __filterGroup: null, _applyModel: function(model) { this._removeAll(); - const group = this.__buttonGroup = new qx.ui.form.RadioGroup().set({ - allowEmptySelection: true - }); + this.__serviceListItem = []; model.toArray().forEach(service => { - const button = new osparc.service.ServiceListItem(service); + const item = new osparc.service.ServiceListItem(service); if (this.__filterGroup !== null) { - button.subscribeToFilterGroup(this.__filterGroup); + item.subscribeToFilterGroup(this.__filterGroup); } - group.add(button); - this._add(button); - button.addListener("dbltap", () => { - this.fireDataEvent("serviceAdd", button.getService()); - }, this); - button.addListener("keypress", e => { + this._add(item); + item.addListener("tap", () => this.__setSelected(item)); + item.addListener("dbltap", () => this.fireDataEvent("serviceAdd", item.getService()), this); + item.addListener("keypress", e => { if (e.getKeyIdentifier() === "Enter") { - this.fireDataEvent("serviceAdd", button.getService()); + this.fireDataEvent("serviceAdd", item.getService()); } }, this); }); - - group.addListener("changeValue", e => this.dispatchEvent(e.clone()), this); }, /** @@ -88,37 +81,41 @@ qx.Class.define("osparc.service.ServiceList", { * @return Returns the model of the selected service or null if selection is empty. */ getSelected: function() { - if (this.__buttonGroup && this.__buttonGroup.getSelection().length) { - return this.__buttonGroup.getSelection()[0].getService(); + const items = this._getChildren(); + for (let i=0; i item.setSelected(item === selectedItem)); + this.fireDataEvent("changeSelected", selectedItem); + }, + /** * Function checking if the selection is empty or not * * @return True if no item is selected, false if there one or more item selected. */ isSelectionEmpty: function() { - if (this.__buttonGroup == null) { - return true; - } - return this.__buttonGroup.getSelection().length === 0; + const selecetedItems = this._getChildren().filter(item => item.getSelected()); + selecetedItems.length === 0; }, /** * Function that selects the first visible button. */ selectFirstVisible: function() { - if (this._hasChildren()) { - const buttons = this._getChildren(); - let current = buttons[0]; - let i = 1; - while (i this.__itemSelected(e.getData())); + this.bind("selected", this, "backgroundColor", { + converter: selected => selected ? "strong-main" : "info" + }); }, properties: { diff --git a/services/static-webserver/client/source/class/osparc/theme/Appearance.js b/services/static-webserver/client/source/class/osparc/theme/Appearance.js index 16facaa2949..7f6f8ccfe51 100644 --- a/services/static-webserver/client/source/class/osparc/theme/Appearance.js +++ b/services/static-webserver/client/source/class/osparc/theme/Appearance.js @@ -19,6 +19,30 @@ qx.Theme.define("osparc.theme.Appearance", { extend: osparc.theme.common.Appearance, appearances: { + "dragdrop-no-cursor": { + style: () => { + return { + source: "", + } + } + }, + + "dragdrop-own-cursor": { + style: states => { + let icon = ""; + if (states.move) { + icon = "@FontAwesome5Solid/check/14"; + } else { + icon = "@FontAwesome5Solid/times/14"; + } + return { + source: icon, + position: "right-top", + offset: [12, 0, 0, 12], + } + } + }, + "material-button-invalid": {}, "pb-list": { include: "list", diff --git a/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js b/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js index b9dd0867a4c..682e2d00fe0 100644 --- a/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js +++ b/services/static-webserver/client/source/class/osparc/workbench/ServiceCatalog.js @@ -136,7 +136,7 @@ qx.Class.define("osparc.workbench.ServiceCatalog", { }); scrolledServices.add(serviceList); - this.__serviceList.addListener("changeValue", e => { + this.__serviceList.addListener("changeSelected", e => { if (e.getData() && e.getData().getService()) { const selectedService = e.getData().getService(); this.__changedSelection(selectedService.getKey());