Skip to content

Commit 7a0f575

Browse files
authored
Merge pull request #77 from OCNS/detail_view_improvements
Detail view improvements
2 parents 904563a + b86ea4d commit 7a0f575

File tree

4 files changed

+134
-125
lines changed

4 files changed

+134
-125
lines changed

assets/messages.svg

Lines changed: 1 addition & 0 deletions
Loading

graph.js

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -49,49 +49,41 @@ function layoutNodes() {
4949
cy_layout.run();
5050
}
5151

52+
const BUTTON_ICONS = {
53+
"source": "github.svg",
54+
"documentation": "book.svg",
55+
"homepage": "home.svg",
56+
"download": "download.svg",
57+
"chat": "messages.svg",
58+
"issue tracker": "check-circle.svg",
59+
"forum": "users.svg",
60+
"examples": "code.svg",
61+
"tutorial": "user.svg",
62+
"installation": "package.svg",
63+
"email": "mail.svg"
64+
}
65+
66+
const BUTTON_ROWS = [
67+
["homepage", "download", "source"],
68+
["documentation", "installation", "tutorial", "examples"],
69+
["forum", "issue tracker", "chat", "email"]
70+
];
71+
5272
function urlButton(type, url) {
5373
const button = document.createElement("button");
54-
let icon = "";
55-
switch (type) {
56-
case "source":
57-
iconFile = "github.svg";
58-
break;
59-
case "documentation":
60-
iconFile = "book.svg";
61-
break;
62-
case "homepage":
63-
iconFile = "home.svg";
64-
break;
65-
case "download":
66-
iconFile = "download.svg";
67-
break;
68-
case "issue tracker":
69-
iconFile = "check-circle.svg";
70-
break;
71-
case "forum":
72-
iconFile = "users.svg";
73-
break;
74-
case "examples":
75-
iconFile = "code.svg";
76-
break;
77-
case "tutorial":
78-
iconFile = "user.svg";
79-
break;
80-
case "installation":
81-
iconFile = "package.svg";
82-
break;
83-
case "email":
84-
iconFile = "mail.svg";
85-
break;
86-
default:
87-
iconFile = "link.svg";
88-
}
74+
let iconFile = BUTTON_ICONS[type];
8975
button.type = "button"
90-
button.classList.add('btn', 'btn-info', 'm-1');
91-
icon = `<img aria-hidden='true' focusable='false' class='icon' src='assets/${iconFile}'></img>`;
76+
button.classList.add('btn', 'btn-sm', 'm-1');
77+
let icon = `<img aria-hidden='true' focusable='false' class='icon' src='assets/${iconFile}'></img>`;
9278
button.innerHTML = icon + " " + type;
93-
button.onclick = function() {
94-
window.open(url, "_blank");
79+
if (url !== undefined) {
80+
button.classList.add('btn-info');
81+
button.onclick = function() {
82+
window.open(url, "_blank");
83+
}
84+
} else {
85+
button.classList.add('btn-secondary');
86+
button.disabled = true;
9587
}
9688
return button;
9789
}
@@ -139,13 +131,20 @@ function showNodeDetails(node) {
139131
showDetails(null, null);
140132
} else {
141133
showDetails(node.data(), node.outgoers("edge").map((edge) => {
142-
return {target: edge.target().id(), label: edge.data("label"), source: edge.source().id()};
143-
}));
134+
return { type: "outgoing", target: edge.target().id(), label: edge.data("label"), source: edge.source().id() };
135+
}).concat(
136+
node.incomers("edge").map((edge) => {
137+
return { type: "incoming", target: edge.target().id(), label: edge.data("label"), source: edge.source().id() }
138+
})
139+
)
140+
);
144141
}
145142
}
146143

147144
function highlightEdge(edge) {
148-
const details = document.getElementById("details");
145+
const details_top = document.getElementById("details_top");
146+
const details_bottom = document.getElementById("details_bottom");
147+
details_bottom.innerHTML = "";
149148
const headerElement = document.createElement("h2");
150149
headerElement.innerHTML = edge.id();
151150

@@ -159,16 +158,16 @@ function highlightEdge(edge) {
159158
targetLink.addEventListener("click",function(e) { edge.unselect(); edge.target().select(); });
160159
targetLink.innerHTML = edge.target().id();
161160

162-
details.innerHTML = "";
161+
details_top.innerHTML = "";
163162

164163
const paragraph = document.createElement("p");
165164
paragraph.appendChild(sourceLink);
166165
const label = document.createElement("i");
167166
label.innerHTML = " " + edge.data("label") + " ";
168167
paragraph.appendChild(label);
169168
paragraph.appendChild(targetLink);
170-
details.appendChild(headerElement);
171-
details.appendChild(paragraph);
169+
details_top.appendChild(headerElement);
170+
details_top.appendChild(paragraph);
172171
// Only show the edge and the connected nodes
173172
cy.elements().forEach(n => n.style("opacity", 0.2));
174173
edge.style("opacity", 1);

index.html

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ <h5 class="offcanvas-title" id="filter_pane_label">Highlight simulators</h5>
6767
<div class="col col-8" style="height: 80vh" id="cy"><br /></div>
6868

6969
<!-- details pane -->
70-
<div class="col col-4" style="height: 80vh; overflow: scroll" id="details"></div>
70+
<div class="col col-4 d-flex flex-column justify-content-between" style="height: 80vh; overflow: scroll" id="details">
71+
<div id="details_top" style="overflow-y: scroll; max-height: 80vh"></div>
72+
<div id="details_bottom"></div>
73+
</div>
7174
</div>
7275
</div>
7376

index.js

Lines changed: 85 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -8,108 +8,114 @@ var TOOL_DESCRIPTIONS = {};
88
const selected = [];
99

1010
// If params are null, show a default message
11-
function showDetails(data, outgoers) {
11+
function showDetails(data, connected) {
1212
// Show details about the simulator
13-
const details = document.getElementById("details");
13+
const details_top = document.getElementById("details_top");
14+
const details_bottom = document.getElementById("details_bottom");
1415
// Basic description
1516
if (data === null) {
16-
details.innerHTML = "<br />";
17-
details.innerHTML += "<h2>Using this resource</h2>";
18-
details.innerHTML += "<ul>";
19-
details.innerHTML += "<li>Use the 'Toggle Filters' button to activate the simulation engine filter.</li>";
20-
details.innerHTML += "<li>Select what simulation engines you would like to show in the graph.</li>";
21-
details.innerHTML += "<li>Select a node/edge to see its ecosystem in the graph.</li>";
22-
details.innerHTML += "<li>Double click/tap on a node/edge to see details of the tool.</li>";
23-
details.innerHTML += "<li>Click outside to unselect nodes.</li>";
24-
details.innerHTML += "</ul>";
25-
details.innerHTML += "<h3 class='mt-3'>Contributing</h2>";
26-
details.innerHTML += `<p>Contributions are welcome! If you have anything to add or correct in the data,
17+
details_top.innerHTML = "<br />";
18+
details_top.innerHTML += "<h2>Using this resource</h2>";
19+
details_top.innerHTML += "<ul>";
20+
details_top.innerHTML += "<li>Use the 'Toggle Filters' button to activate the simulation engine filter.</li>";
21+
details_top.innerHTML += "<li>Select what simulation engines you would like to show in the graph.</li>";
22+
details_top.innerHTML += "<li>Select a node/edge to see its ecosystem in the graph.</li>";
23+
details_top.innerHTML += "<li>Double click/tap on a node/edge to see details of the tool.</li>";
24+
details_top.innerHTML += "<li>Click outside to unselect nodes.</li>";
25+
details_top.innerHTML += "</ul>";
26+
details_top.innerHTML += "<h3 class='mt-3'>Contributing</h2>";
27+
details_top.innerHTML += `<p>Contributions are welcome! If you have anything to add or correct in the data,
2728
please follow the link at the end of the tool's details view to edit the data on GitHub.
2829
You can also open an <a href='${REPO_URL}/issues'>issue on the GitHub repository</a>.</p>`;
29-
details.innerHTML += "<h3 class='mt-3'>List of simulators</h2>";
30-
details.innerHTML += "<div class='d-flex'>";
30+
details_bottom.innerHTML = "<div class='d-flex'>";
31+
details_bottom.innerHTML += "<h3 class='mt-3'>List of simulators</h2>";
3132
for (const sim of SIMULATORS) {
3233
const quoted_sim = `[id='${sim}']`;
33-
details.innerHTML += `<button class='btn btn-secondary m-1' onclick="cy.nodes('#simulators').unselect(); let node = cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'); node.select(); showNodeDetails(node);">${TOOL_DESCRIPTIONS[sim].short_name}</button>`;
34+
details_bottom.innerHTML += `<button class='btn btn-secondary m-1' onclick="cy.nodes('#simulators').unselect(); let node = cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'); node.select(); showNodeDetails(node);">${TOOL_DESCRIPTIONS[sim].short_name}</button>`;
3435
}
35-
details.innerHTML += "</div>";
36-
36+
details_bottom.innerHTML += "</div>";
37+
window.history.pushState({}, "", window.location.href.split("?")[0]);
38+
return;
3739
}
38-
else {
39-
if (data["features"].includes("simulator")) {
40-
const quoted_sim = `[id='${data.id}']`;
41-
details.innerHTML = `<div class='d-flex justify-content-between align-items-center'>
42-
<h2>${data["full_name"]}</h2>
43-
<button class='btn btn-outline-primary align-middle' title='Center ${data["short_name"]} in the graph' onclick="highlightNode(cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'));">
44-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bullseye" viewBox="0 0 16 16">
45-
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
46-
<path d="M8 13A5 5 0 1 1 8 3a5 5 0 0 1 0 10m0 1A6 6 0 1 0 8 2a6 6 0 0 0 0 12"/>
47-
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8"/>
48-
<path d="M9.5 8a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
49-
</svg>
50-
</button>
51-
</div>`;
52-
} else {
53-
details.innerHTML = `<h2>${data["full_name"]}</h2>`;
54-
}
55-
details.innerHTML += "<p>" + data["description"] + "</p>";
40+
details_top.innerHTML = "";
41+
details_bottom.innerHTML = "";
42+
let description = document.createElement("div");
43+
if (data["features"].includes("simulator")) {
44+
const quoted_sim = `[id='${data.id}']`;
45+
description.innerHTML = `<div class='d-flex justify-content-between align-items-center'>
46+
<h2>${data["full_name"]}</h2>
47+
<button class='btn btn-outline-primary align-middle' title='Center ${data["short_name"]} in the graph' onclick="highlightNode(cy.nodes('${quoted_sim.replace(/'/g, "\\'")}'));">
48+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-bullseye" viewBox="0 0 16 16">
49+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
50+
<path d="M8 13A5 5 0 1 1 8 3a5 5 0 0 1 0 10m0 1A6 6 0 1 0 8 2a6 6 0 0 0 0 12"/>
51+
<path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6m0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8"/>
52+
<path d="M9.5 8a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
53+
</svg>
54+
</button>
55+
</div>`;
56+
} else {
57+
description.innerHTML = `<h2>${data["full_name"]}</h2>`;
5658
}
59+
description.innerHTML += "<p>" + data["description"] + "</p>";
5760
// Relations
58-
if (outgoers !== null) {
59-
if (outgoers.length > 0) {
60-
details.innerHTML += "<h3>Relations</h3>";
61+
if (connected !== null) {
62+
if (connected.length > 0) {
63+
description.innerHTML += "<h3>Relations</h3>";
6164
const list = document.createElement("ul");
62-
for (let edge of outgoers) {
65+
for (let edge of connected) {
66+
if (edge["source"] === "simulators") {
67+
continue;
68+
}
6369
const listItem = document.createElement("li");
6470
const targetLink = document.createElement("a");
65-
// targetLink.href = "#";
66-
// targetLink.addEventListener("click",function(e) { node.unselect(); edge.target().select(); });
67-
targetLink.innerHTML = edge["target"];
68-
const label = document.createElement("i");
71+
targetLink.href = "#";
72+
if (edge["type"] === "outgoing") {
73+
targetId = edge["target"];
74+
} else {
75+
targetId = edge["source"];
76+
}
77+
targetLink.addEventListener("click",function(e) {
78+
console.log("Clicked on " + targetLink.innerHTML);
79+
cy.nodes("[id='" + data.id + "']").unselect();
80+
cy.nodes("[id='" + targetLink.innerHTML + "']").select();
81+
});
82+
targetLink.innerHTML = targetId;
83+
const simName = document.createElement("i");
84+
simName.innerHTML = data["short_name"];
85+
const label = document.createElement("span");
6986
label.innerHTML = " " + edge["label"] + " ";
70-
listItem.appendChild(label);
71-
listItem.appendChild(targetLink);
72-
87+
if (edge["type"] === "outgoing") {
88+
listItem.append(simName);
89+
listItem.append(label);
90+
listItem.appendChild(targetLink);
91+
} else {
92+
listItem.appendChild(targetLink);
93+
listItem.append(label);
94+
listItem.append(simName);
95+
}
7396
list.appendChild(listItem);
7497
}
75-
details.appendChild(list);
98+
description.appendChild(list);
7699
}
100+
details_top.appendChild(description);
77101
// URLs
78102
link_heading = document.createElement("h3");
79103
link_heading.innerHTML = "Links";
80-
details.append(link_heading);
81-
if (data["urls"] !== undefined) {
82-
for (let [text, url] of Object.entries(data["urls"])) {
83-
details.appendChild(urlButton(text, url));
104+
let tool_links = data["urls"];
105+
details_bottom.appendChild(link_heading);
106+
for (let row_idx=0; row_idx < BUTTON_ROWS.length; row_idx++) {
107+
let row = document.createElement("div");
108+
row.classList.add("row");
109+
// Go through elements in BUTTON_ROWS
110+
for (const button_type of BUTTON_ROWS[row_idx]) {
111+
let col = document.createElement("div");
112+
col.classList.add("col-auto");
113+
let button = urlButton(button_type, tool_links[button_type]);
114+
col.appendChild(button);
115+
row.appendChild(col);
84116
}
117+
details_bottom.appendChild(row);
85118
}
86-
// Back to simulators
87-
back_p = document.createElement("p");
88-
back_p.classList.add("mt-3");
89-
back_button = document.createElement("a");
90-
back_button.href = "#";
91-
back_button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
92-
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.417A6 6 0 1 0 8 2z"/>
93-
<path d="M8 4.466V.534a.25.25 0 0 0-.41-.192L5.23 2.308a.25.25 0 0 0 0 .384l2.36 1.966A.25.25 0 0 0 8 4.466"/>
94-
</svg>&nbsp;Back to simulators`;
95-
back_button.classList.add("btn", "btn-secondary");
96-
back_button.onclick = function() { cy.nodes(`[id = '${data.id}']`).unselect(); cy.nodes("#simulators").select(); unhighlightNode(); };
97-
back_p.appendChild(back_button);
98-
details.appendChild(back_p);
99-
// Edit footer
100-
edit_p = document.createElement("p");
101-
edit_p.classList.add("mt-3", "text-end");
102-
edit_link = document.createElement("a");
103-
edit_link.classList.add("link-secondary");
104-
edit_link.href = `${REPO_URL}/edit/${GIT_BRANCH}/${DATA_FOLDER}/${data["short_name"].replaceAll(" ", "-")}.yaml`;
105-
edit_link.innerHTML = "Edit this description on GitHub&nbsp;";
106-
edit_link.innerHTML += `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-github" viewBox="0 0 16 16">
107-
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
108-
</svg>`;
109-
edit_link.target = "_blank";
110-
edit_p.appendChild(edit_link);
111-
details.appendChild(edit_p);
112-
113119
}
114120
// hide filter pane
115121
const filterPane = new bootstrap.Offcanvas('#filter_pane');

0 commit comments

Comments
 (0)