Skip to content

Commit 8fd4e45

Browse files
authored
Dashboard UI/UX: Ease thumbnail assignation (ITISFoundation#2853)
1 parent 4e46d66 commit 8fd4e45

File tree

13 files changed

+301
-108
lines changed

13 files changed

+301
-108
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/* ************************************************************************
2+
3+
osparc - the simcore frontend
4+
5+
https://osparc.io
6+
7+
Copyright:
8+
2022 IT'IS Foundation, https://itis.swiss
9+
10+
License:
11+
MIT: https://opensource.org/licenses/MIT
12+
13+
Authors:
14+
* Odei Maiz (odeimaiz)
15+
16+
************************************************************************ */
17+
18+
qx.Class.define("osparc.component.editor.ThumbnailEditor", {
19+
extend: qx.ui.core.Widget,
20+
21+
construct: function(url, suggestions = []) {
22+
this.base(arguments);
23+
24+
this._setLayout(new qx.ui.layout.VBox(8));
25+
26+
this.getChildControl("url-field");
27+
this.getChildControl("scroll-thumbnails");
28+
const suggestionsLayout = this.getChildControl("thumbnails-layout");
29+
suggestionsLayout.exclude();
30+
31+
this.getChildControl("cancel-btn");
32+
this.getChildControl("save-btn");
33+
34+
if (url) {
35+
this.setUrl(url);
36+
}
37+
if (suggestions) {
38+
this.setSuggestions(suggestions);
39+
}
40+
},
41+
42+
properties: {
43+
url: {
44+
check: "String",
45+
init: "",
46+
nullable: false,
47+
event: "changeUrl"
48+
},
49+
50+
suggestions: {
51+
check: "Array",
52+
init: [],
53+
nullable: true,
54+
event: "changeSuggestions",
55+
apply: "__applySuggestions"
56+
}
57+
},
58+
59+
events: {
60+
"updateThumbnail": "qx.event.type.Data",
61+
"cancel": "qx.event.type.Event"
62+
},
63+
64+
statics: {
65+
sanitizeUrl: function(dirty) {
66+
const clean = osparc.wrapper.DOMPurify.getInstance().sanitize(dirty);
67+
if ((dirty && dirty !== clean) || (clean !== "" && !osparc.utils.Utils.isValidHttpUrl(clean))) {
68+
osparc.component.message.FlashMessenger.getInstance().logAs(this.tr("Error checking link"), "WARNING");
69+
return null;
70+
}
71+
return clean;
72+
}
73+
},
74+
75+
members: {
76+
_createChildControlImpl: function(id) {
77+
let control;
78+
switch (id) {
79+
case "url-field":
80+
control = new qx.ui.form.TextField().set({
81+
font: "text-14",
82+
backgroundColor: "background-main",
83+
placeholder: this.tr("url")
84+
});
85+
this.bind("url", control, "value");
86+
this._add(control);
87+
break;
88+
case "thumbnails-layout": {
89+
control = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
90+
const label = new qx.ui.basic.Label(this.tr("or pick one from the list of services:"));
91+
control.add(label);
92+
this._add(control, {
93+
flex: 1
94+
});
95+
break;
96+
}
97+
case "scroll-thumbnails": {
98+
const thumbnailsLayout = this.getChildControl("thumbnails-layout");
99+
control = new qx.ui.container.SlideBar().set({
100+
alignX: "center",
101+
maxHeight: 170
102+
});
103+
thumbnailsLayout.add(control);
104+
[
105+
control.getChildControl("button-backward"),
106+
control.getChildControl("button-forward")
107+
].forEach(btn => {
108+
btn.set({
109+
maxWidth: 30,
110+
maxHeight: 30,
111+
alignY: "middle",
112+
marginLeft: 5,
113+
marginRight: 5,
114+
icon: "@FontAwesome5Solid/ellipsis-h/16",
115+
backgroundColor: "transparent"
116+
});
117+
});
118+
control.setLayout(new qx.ui.layout.HBox(5).set({
119+
alignX: "center"
120+
}));
121+
break;
122+
}
123+
case "buttons-layout":
124+
control = new qx.ui.container.Composite(new qx.ui.layout.HBox(10).set({
125+
alignX: "right"
126+
}));
127+
this._add(control);
128+
break;
129+
case "cancel-btn": {
130+
const buttons = this.getChildControl("buttons-layout");
131+
control = new qx.ui.form.Button(this.tr("Cancel"));
132+
control.addListener("execute", () => this.fireEvent("cancel"), this);
133+
buttons.add(control);
134+
break;
135+
}
136+
case "save-btn": {
137+
const buttons = this.getChildControl("buttons-layout");
138+
control = new qx.ui.form.Button(this.tr("Save"));
139+
control.addListener("execute", () => {
140+
const urlField = this.getChildControl("url-field");
141+
const validUrl = this.self().sanitizeUrl(urlField.getValue());
142+
if (validUrl) {
143+
this.fireDataEvent("updateThumbnail", validUrl);
144+
}
145+
}, this);
146+
buttons.add(control);
147+
break;
148+
}
149+
}
150+
151+
return control || this.base(arguments, id);
152+
},
153+
154+
__applySuggestions: function(suggestions) {
155+
const thumbnailsLayout = this.getChildControl("scroll-thumbnails");
156+
thumbnailsLayout.removeAll();
157+
suggestions.forEach(suggestion => {
158+
const thumbnail = new osparc.ui.basic.Thumbnail(suggestion, 170, 124);
159+
thumbnail.addListener("tap", () => {
160+
this.setUrl(thumbnail.getChildControl("image").getSource());
161+
thumbnailsLayout.getChildren().forEach(thumbnailImg => osparc.utils.Utils.removeBorder(thumbnailImg));
162+
osparc.utils.Utils.addBorder(thumbnail, 1, "#007fd4"); // Visual Studio blue
163+
});
164+
thumbnailsLayout.add(thumbnail);
165+
});
166+
this.getChildControl("thumbnails-layout").setVisibility(suggestions.length ? "visible" : "excluded");
167+
}
168+
}
169+
});

services/web/client/source/class/osparc/component/widget/NodeTreeItem.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -226,15 +226,11 @@ qx.Class.define("osparc.component.widget.NodeTreeItem", {
226226
},
227227

228228
__setHoveredStyle: function() {
229-
this.getContentElement().setStyles({
230-
"border": "1px solid " + qx.theme.manager.Color.getInstance().resolve("background-selected")
231-
});
229+
osparc.utils.Utils.addBorder(this, 1, qx.theme.manager.Color.getInstance().resolve("background-selected"));
232230
},
233231

234232
__setNotHoveredStyle: function() {
235-
this.getContentElement().setStyles({
236-
"border": "1px solid transparent"
237-
});
233+
osparc.utils.Utils.removeBorder(this);
238234
}
239235
}
240236
});

services/web/client/source/class/osparc/data/model/Node.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ qx.Class.define("osparc.data.model.Node", {
213213
},
214214

215215
statics: {
216+
isFrontend: function(metaData) {
217+
return (metaData && metaData.key && metaData.key.includes("/frontend/"));
218+
},
219+
216220
isFilePicker: function(metaData) {
217221
return (metaData && metaData.key && metaData.key.includes("file-picker"));
218222
},

services/web/client/source/class/osparc/data/model/Workbench.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ qx.Class.define("osparc.data.model.Workbench", {
8585
__rootNodes: null,
8686
__edges: null,
8787

88+
getWorkbenchInitData: function() {
89+
return this.__workbenchInitData;
90+
},
91+
8892
buildWorkbench: function() {
8993
this.__rootNodes = {};
9094
this.__edges = {};

services/web/client/source/class/osparc/desktop/WorkbenchView.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,7 @@ qx.Class.define("osparc.desktop.WorkbenchView", {
260260
alignY: "middle",
261261
backgroundColor
262262
});
263-
tabPageBtn.getContentElement().setStyles({
264-
"border": "0px"
265-
});
263+
osparc.utils.Utils.removeBorder(tabPageBtn);
266264
tabPageBtn.bind("value", tabPageBtn, "backgroundColor", {
267265
converter: val => val ? backgroundColor : "contrasted-background+"
268266
});

services/web/client/source/class/osparc/navigation/BreadcrumbsSlideshow.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,27 +62,21 @@ qx.Class.define("osparc.navigation.BreadcrumbsSlideshow", {
6262
textColor: "text-disabled",
6363
backgroundColor: "transparent"
6464
});
65-
button.getContentElement().setStyles({
66-
"border": "1px solid " + colorManager.resolve("text")
67-
});
65+
osparc.utils.Utils.addBorder(button, 1, colorManager.resolve("text"));
6866
} else if (button.getValue() === false) {
6967
// enabled but not current
7068
button.set({
7169
textColor: "text",
7270
backgroundColor: "transparent"
7371
});
74-
button.getContentElement().setStyles({
75-
"border": "1px solid " + colorManager.resolve("text")
76-
});
72+
osparc.utils.Utils.addBorder(button, 1, colorManager.resolve("text"));
7773
} else {
7874
// current
7975
button.set({
8076
textColor: "text",
8177
backgroundColor: "background-main-lighter+"
8278
});
83-
button.getContentElement().setStyles({
84-
"border": "0px"
85-
});
79+
osparc.utils.Utils.removeBorder(button);
8680
}
8781
};
8882

services/web/client/source/class/osparc/servicecard/Large.js

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ qx.Class.define("osparc.servicecard.Large", {
9898
thumbnailWidth = Math.min(thumbnailWidth - 20, this.self().THUMBNAIL_MAX_WIDTH);
9999
const thumbnail = this.__createThumbnail(thumbnailWidth, maxThumbnailHeight);
100100
const thumbnailLayout = this.__createViewWithEdit(thumbnail, this.__openThumbnailEditor);
101+
thumbnailLayout.getLayout().set({
102+
alignX: "center"
103+
});
101104

102105
const hBox = new qx.ui.container.Composite(new qx.ui.layout.HBox(3).set({
103106
alignX: "center"
@@ -109,8 +112,9 @@ qx.Class.define("osparc.servicecard.Large", {
109112
this._add(hBox);
110113

111114
const description = this.__createDescription();
112-
const descriptionLayout = this.__createViewWithEdit(description, this.__openDescriptionEditor);
113-
this._add(descriptionLayout);
115+
const editInTitle = this.__createViewWithEdit(description.getChildren()[0], this.__openDescriptionEditor);
116+
description.addAt(editInTitle, 0);
117+
this._add(description);
114118

115119
const rawMetadata = this.__createRawMetadata();
116120
const more = new osparc.desktop.PanelView(this.tr("raw metadata"), rawMetadata).set({
@@ -127,14 +131,10 @@ qx.Class.define("osparc.servicecard.Large", {
127131
const layout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5).set({
128132
alignY: "middle"
129133
}));
130-
layout.add(view, {
131-
flex: 1
132-
});
134+
layout.add(view);
133135
if (this.__isOwner()) {
134136
const editBtn = osparc.utils.Utils.getEditButton();
135-
editBtn.addListener("execute", () => {
136-
cb.call(this);
137-
}, this);
137+
editBtn.addListener("execute", () => cb.call(this), this);
138138
layout.add(editBtn);
139139
}
140140

@@ -317,21 +317,16 @@ qx.Class.define("osparc.servicecard.Large", {
317317

318318
__openThumbnailEditor: function() {
319319
const title = this.tr("Edit Thumbnail");
320-
const thubmnailEditor = new osparc.component.widget.Renamer(this.getService()["thumbnail"], null, title);
321-
thubmnailEditor.addListener("labelChanged", e => {
322-
thubmnailEditor.close();
323-
const dirty = e.getData()["newLabel"];
324-
const clean = osparc.wrapper.DOMPurify.getInstance().sanitize(dirty);
325-
if ((dirty && dirty !== clean) || (clean !== "" && !osparc.utils.Utils.isValidHttpUrl(clean))) {
326-
osparc.component.message.FlashMessenger.getInstance().logAs(this.tr("Error checking thumbnail link"), "WARNING");
327-
} else {
328-
this.__updateService({
329-
"thumbnail": clean
330-
});
331-
}
320+
const thumbnailEditor = new osparc.component.editor.ThumbnailEditor(this.getStudy().getThumbnail());
321+
const win = osparc.ui.window.Window.popUpInWindow(thumbnailEditor, title, 300, 120);
322+
thumbnailEditor.addListener("updateThumbnail", e => {
323+
win.close();
324+
const validUrl = e.getData();
325+
this.__updateService({
326+
"thumbnail": validUrl
327+
});
332328
}, this);
333-
thubmnailEditor.center();
334-
thubmnailEditor.open();
329+
thumbnailEditor.addListener("cancel", () => win.close());
335330
},
336331

337332
__openDescriptionEditor: function() {

services/web/client/source/class/osparc/servicecard/Utils.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,8 @@ qx.Class.define("osparc.servicecard.Utils", {
150150
*/
151151
createThumbnail: function(serviceData, maxWidth, maxHeight = 160) {
152152
const image = new osparc.ui.basic.Thumbnail(null, maxWidth, maxHeight);
153-
const img = image.getChildControl("image");
154-
img.set({
155-
source: "thumbnail" in serviceData && serviceData["thumbnail"] !== "" ? serviceData["thumbnail"] : osparc.dashboard.GridButtonItem.SERVICE_ICON
153+
image.set({
154+
source: "thumbnail" in serviceData && serviceData["thumbnail"] !== "" ? serviceData["thumbnail"] : osparc.dashboard.CardBase.SERVICE_ICON
156155
});
157156
return image;
158157
},

0 commit comments

Comments
 (0)