Skip to content

Commit

Permalink
✨ [Frontend] Drag&Drop: Projects and Folders (#6957)
Browse files Browse the repository at this point in the history
  • Loading branch information
odeimaiz authored Dec 16, 2024
1 parent a7d1e3a commit 854af6e
Show file tree
Hide file tree
Showing 38 changed files with 777 additions and 324 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(), {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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: {
Expand Down Expand Up @@ -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,
Expand All @@ -246,7 +262,8 @@ qx.Class.define("osparc.dashboard.CardBase", {

resourceType: {
check: ["study", "template", "service"],
nullable: false,
init: true,
nullable: true,
event: "changeResourceType"
},

Expand Down Expand Up @@ -365,7 +382,7 @@ qx.Class.define("osparc.dashboard.CardBase", {
check: "Boolean",
init: false,
nullable: false,
apply: "_applyMultiSelectionMode"
apply: "__applyMultiSelectionMode"
},

fetching: {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -22,28 +22,38 @@ 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.");
}
},

/**
* 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());
},
Expand All @@ -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());
},

/**
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
});
Loading

0 comments on commit 854af6e

Please sign in to comment.