Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ [Frontend] Drag&Drop: Projects and Folders #6957

Merged
merged 76 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
565681c
set layout first
odeimaiz Dec 12, 2024
20e3f3d
isValidWidget
odeimaiz Dec 12, 2024
955bf5d
[skip ci] ToggleButton -> Widget
odeimaiz Dec 12, 2024
be05ce1
[skip ci] minor
odeimaiz Dec 12, 2024
e5affe5
[skip ci] tap service button
odeimaiz Dec 12, 2024
011f9de
[skip ci] don't listen to change value
odeimaiz Dec 12, 2024
84ba8f7
[skip ci] __evalSelectedButton
odeimaiz Dec 12, 2024
7b89f36
Merge branch 'master' into feature/daraga-doropa
odeimaiz Dec 12, 2024
23a0aae
empty specific-info by default
odeimaiz Dec 12, 2024
4302f5d
not needed
odeimaiz Dec 12, 2024
b0e51d5
[skip ci] minor
odeimaiz Dec 12, 2024
8719ce8
simpler
odeimaiz Dec 12, 2024
63d496b
minors
odeimaiz Dec 12, 2024
dea32d2
refactor
odeimaiz Dec 12, 2024
4f6f2b5
[skip ci] minor
odeimaiz Dec 12, 2024
fdc8a5c
[skip ci] cleanup
odeimaiz Dec 12, 2024
7850d4f
rename files
odeimaiz Dec 12, 2024
fe21854
[skip ci] rename
odeimaiz Dec 12, 2024
0e44843
[skip ci] renaming
odeimaiz Dec 12, 2024
44713c9
prettifyMenus
odeimaiz Dec 12, 2024
288ef49
[skip ci] multiselection working
odeimaiz Dec 13, 2024
a51fc68
support null first and last name
odeimaiz Dec 13, 2024
744d96f
[skip ci] attachDragHandlers and attachDropHandlers
odeimaiz Dec 13, 2024
4022440
Merge branch 'master' into feature/daraga-doropa
odeimaiz Dec 13, 2024
1f68f7c
[skip ci] minor
odeimaiz Dec 13, 2024
35ab99d
Merge branch 'feature/daraga-doropa' of github.com:odeimaiz/osparc-si…
odeimaiz Dec 13, 2024
b560585
[skip ci] pass ordingin info in drag event
odeimaiz Dec 13, 2024
3a5bd4a
[skip ci] compatibility checks
odeimaiz Dec 13, 2024
c3622d0
move working
odeimaiz Dec 13, 2024
b3833b5
wire signals
odeimaiz Dec 13, 2024
1ff160a
[skip ci] minor
odeimaiz Dec 13, 2024
10ebce4
move study working
odeimaiz Dec 13, 2024
03f7f0f
Merge branch 'master' into feature/daraga-doropa
odeimaiz Dec 13, 2024
451f3bb
minor
odeimaiz Dec 13, 2024
884f833
not needed
odeimaiz Dec 13, 2024
afe48e5
list items are not toggle buttons
odeimaiz Dec 13, 2024
ed57fb4
smaller TIP disclaimer
odeimaiz Dec 15, 2024
0513e3e
prettify listbuttonitem menu
odeimaiz Dec 15, 2024
97cc262
bgColor
odeimaiz Dec 15, 2024
d61362f
minor
odeimaiz Dec 15, 2024
60037a2
dragIndicator
odeimaiz Dec 15, 2024
1f8f1b9
DragWidget
odeimaiz Dec 15, 2024
2b775c8
folders also use dragWidget
odeimaiz Dec 15, 2024
63fdfdb
highlight folder icon
odeimaiz Dec 15, 2024
da61500
no cursor
odeimaiz Dec 15, 2024
c9926b7
dragdrop-own-cursor
odeimaiz Dec 15, 2024
56b097b
minor aesthetics
odeimaiz Dec 15, 2024
eafb08e
dragend resetTextColor
odeimaiz Dec 15, 2024
a830add
minor
odeimaiz Dec 15, 2024
a847164
setDropAllowed
odeimaiz Dec 15, 2024
1b7d5b8
minor
odeimaiz Dec 15, 2024
3d2361e
renaming
odeimaiz Dec 15, 2024
0f36355
dragStartFolder and dragEnd
odeimaiz Dec 15, 2024
b5b579e
DragDropHelpers
odeimaiz Dec 15, 2024
4a28028
refactor
odeimaiz Dec 15, 2024
839410d
refactoring
odeimaiz Dec 15, 2024
be5def7
refactor
odeimaiz Dec 15, 2024
a7e87c0
dragOver and drop
odeimaiz Dec 15, 2024
4cd636e
more refactoring
odeimaiz Dec 15, 2024
fbc0e42
wirte resourceToFolderRequested
odeimaiz Dec 15, 2024
623a396
dragOver logic
odeimaiz Dec 15, 2024
3031392
checks correct
odeimaiz Dec 15, 2024
5f51f87
minor
odeimaiz Dec 15, 2024
06cef92
minor
odeimaiz Dec 15, 2024
f0a305e
minor
odeimaiz Dec 15, 2024
4652068
minor
odeimaiz Dec 15, 2024
2bfd6d9
signature
odeimaiz Dec 15, 2024
88ed670
__doMoveFolder
odeimaiz Dec 15, 2024
1de0288
moving folders
odeimaiz Dec 15, 2024
96d937e
one more check
odeimaiz Dec 15, 2024
8148f72
doMoveStudy
odeimaiz Dec 15, 2024
5de4500
minor fix
odeimaiz Dec 15, 2024
4bcfe9e
draggingOver
odeimaiz Dec 15, 2024
d9260ed
minor
odeimaiz Dec 15, 2024
a9b5bfc
highlilight compatible icons
odeimaiz Dec 15, 2024
dc30231
Merge branch 'master' into feature/daraga-doropa
odeimaiz Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading