From abc08e2b971b5f4553f754c7a56f6e07213cf674 Mon Sep 17 00:00:00 2001 From: Ugo Date: Thu, 12 Sep 2019 12:59:21 +0200 Subject: [PATCH] Infobox and added save+clear --- frontend/assets/css/src/style.scss | 21 +++- frontend/assets/css/style.min.css | 2 +- frontend/assets/js/canvas.js | 163 +++++++++++++++++------------ frontend/index.html | 11 +- main.py | 68 +++++++----- 5 files changed, 169 insertions(+), 96 deletions(-) diff --git a/frontend/assets/css/src/style.scss b/frontend/assets/css/src/style.scss index 0e10866..1a175ed 100644 --- a/frontend/assets/css/src/style.scss +++ b/frontend/assets/css/src/style.scss @@ -12,7 +12,7 @@ main{ position: relative; height: 100vh; - #infobar{ + #errorbar{ position: absolute; top:0; width: 100%; @@ -106,5 +106,24 @@ main{ left: auto; bottom: 1em; right: 1em; + left: 1em; + display: flex; + justify-content: flex-end; + #infobar{ + position: absolute; + bottom:0; + left: 0; + width: 50%; + padding: 0.4em; + color: rgba(0, 0, 0, 0.8); + font-weight: bold; + font-size: 1.8em; + z-index: 1; + opacity: 0; + transition: opacity 1s; + &.active{ + opacity: 1; + } + } } } diff --git a/frontend/assets/css/style.min.css b/frontend/assets/css/style.min.css index 8a4d3cf..a168ddd 100644 --- a/frontend/assets/css/style.min.css +++ b/frontend/assets/css/style.min.css @@ -1 +1 @@ -*{margin:0;padding:0;box-sizing:border-box;font-family:"Roboto-Regular"}main{position:relative;height:100vh}main #infobar{position:absolute;top:0;width:100%;padding:0.4em;background-color:rgba(232,9,9,0.4);border:4px solid rgba(232,9,9,0.8);text-align:center;font-weight:bold;font-size:1.8em;z-index:1;transform:translateY(-100%);transition:transform 1s}main #infobar.active{transform:translateY(0%)}main .canvas{background-color:lightgrey;position:relative;height:100%}main .canvas .background{position:absolute;top:0;left:0;background-color:rgba(0,0,0,0.5);width:100%;height:100%}main .canvas input{position:absolute;border:none;padding:0.6em 1em;width:200px;font-size:1.2em;text-align:center;transform:translateX(-50%) translateY(-50%)}main nav,main footer{display:flex;position:absolute;top:1em;left:1em;opacity:0.9}main nav button,main footer button{display:block;background-color:#939292;border:none;border-radius:1em;padding:1em 1em;min-width:8em;margin:0.4em;color:black;border:solid 2px #131313;box-shadow:2px 2px 8px rgba(0,0,0,0.5)}main nav button:focus,main footer button:focus{outline:none}main nav button::-moz-focus-inner,main footer button::-moz-focus-inner{border:0}main nav button:hover,main footer button:hover{background-color:#acacac;border-color:#000;cursor:pointer}main nav button.active,main footer button.active{background-color:#f08d19}main nav button h1,main footer button h1{text-align:center;font-size:1.4em;font-weight:normal;white-space:nowrap}main nav button img,main footer button img{display:block;margin:0 auto 0.5em auto;width:2em}main footer{top:auto;left:auto;bottom:1em;right:1em} +*{margin:0;padding:0;box-sizing:border-box;font-family:"Roboto-Regular"}main{position:relative;height:100vh}main #errorbar{position:absolute;top:0;width:100%;padding:0.4em;background-color:rgba(232,9,9,0.4);border:4px solid rgba(232,9,9,0.8);text-align:center;font-weight:bold;font-size:1.8em;z-index:1;transform:translateY(-100%);transition:transform 1s}main #errorbar.active{transform:translateY(0%)}main .canvas{background-color:lightgrey;position:relative;height:100%}main .canvas .background{position:absolute;top:0;left:0;background-color:rgba(0,0,0,0.5);width:100%;height:100%}main .canvas input{position:absolute;border:none;padding:0.6em 1em;width:200px;font-size:1.2em;text-align:center;transform:translateX(-50%) translateY(-50%)}main nav,main footer{display:flex;position:absolute;top:1em;left:1em;opacity:0.9}main nav button,main footer button{display:block;background-color:#939292;border:none;border-radius:1em;padding:1em 1em;min-width:8em;margin:0.4em;color:black;border:solid 2px #131313;box-shadow:2px 2px 8px rgba(0,0,0,0.5)}main nav button:focus,main footer button:focus{outline:none}main nav button::-moz-focus-inner,main footer button::-moz-focus-inner{border:0}main nav button:hover,main footer button:hover{background-color:#acacac;border-color:#000;cursor:pointer}main nav button.active,main footer button.active{background-color:#f08d19}main nav button h1,main footer button h1{text-align:center;font-size:1.4em;font-weight:normal;white-space:nowrap}main nav button img,main footer button img{display:block;margin:0 auto 0.5em auto;width:2em}main footer{top:auto;left:auto;bottom:1em;right:1em;left:1em;display:flex;justify-content:flex-end}main footer #infobar{position:absolute;bottom:0;left:0;width:50%;padding:0.4em;color:rgba(0,0,0,0.8);font-weight:bold;font-size:1.8em;z-index:1;opacity:0;transition:opacity 1s}main footer #infobar.active{opacity:1} diff --git a/frontend/assets/js/canvas.js b/frontend/assets/js/canvas.js index 09d85d8..edc7ccb 100644 --- a/frontend/assets/js/canvas.js +++ b/frontend/assets/js/canvas.js @@ -28,32 +28,32 @@ const node_layout = { shadowOpacity: 0.3, }; +const text_layout = { + fontSize: 22, + fontFamily: 'Roboto, Arial, sans-serif', + fill: 'black', + verticalAlign: 'middle', + align: 'center' +}; + const node_text_layout = { + ...text_layout, x: 1 - node_size, y: 2 - node_size, - text: name, - fontSize: 22, width: 2 * node_size - 1, height: 2 * node_size - 1, - fontFamily: 'Roboto, Arial, Verdana, sans-serif', - fill: 'black', - verticalAlign: 'middle', - align: 'center' + }; const edge_layout = { stroke: 'black', strokeWidth: 3, -} +}; const edge_text_layout = { - fontSize: 22, + ...text_layout, width: 2 * node_size, height: 2 * node_size, - fontFamily: 'Roboto, Arial, Verdana, sans-serif', - fill: 'black', - verticalAlign: 'middle', - align: 'center' }; @@ -118,8 +118,8 @@ function sort(array) { }) } -function show_error(msg) { - let infobar = document.querySelector("#infobar"); +function show_info(msg, elem="#errorbar") { + let infobar = document.querySelector(elem); infobar.innerHTML = msg; infobar.classList.add("active"); window.setTimeout(function() { @@ -127,7 +127,6 @@ function show_error(msg) { }, 4000); } - //----------------------------------------------------------------------------- //---------------------------draw methods-------------------------------------- //----------------------------------------------------------------------------- @@ -173,7 +172,6 @@ function draw_new_node(name, x, y) { node_layer.draw(); } - function draw_new_edge(edge) { let start_node = stage.findOne('#' + edge["start"]); let end_node = stage.findOne('#' + edge["end"]); @@ -264,6 +262,8 @@ function draw_path(path) { path_elements[id].children[0].strokeWidth(node_layout.strokeWidth + 1); path_elements[id].children[0].stroke("rgb(240, 141, 25)"); } + node_layer.draw(); + edge_layer.draw(); } function on_node_click(e) { @@ -281,19 +281,58 @@ function on_node_click(e) { draw_selected_nodes(); }; + +function create_input(x, y) { + return new Promise(function(resolve, reject) { + var container = document.createElement("div"); + container.classList.add("background"); + var input = document.createElement("input"); + var clear = function() { + container.remove(); + stage.listening(true); + focus = true; + } + input.setAttribute("type", "text"); + input.style.left = x + "px"; + input.style.top = y + "px"; + canvas.appendChild(container); + container.appendChild(input); + input.focus(); + stage.listening(false); + focus = false; + window.addEventListener("keyup", function(event) { + if (event.key === "Escape") { + reject(); + clear(); + } + }); + input.addEventListener("keyup", function(event) { + if (event.key === "Enter") { + resolve(input.value); + clear(); + } + }); + }); +} + //----------------------------------------------------------------------------- //---------------------------REST methods-------------------------------------- //----------------------------------------------------------------------------- -function post(endpoint, data) { - return fetch(url + endpoint, { - method: 'POST', - body: JSON.stringify(data), +function rest_req(method, endpoint, data) { + let body_data = {} + if(!!data){ + body_data = {body: JSON.stringify(data)}; + } + return fetch(url + "api/" + endpoint, { + method: method, + ...body_data, headers: { 'Content-Type': 'application/json' } }).then(res => res.json()) } +//----------------------------------------------------------------------------- function create_node() { if (state != "create_node") { state = "create_node"; @@ -303,12 +342,12 @@ function create_node() { y: (Math.random()-0.5)*node_size*4+height / 2 }; create_input(pos.x, pos.y).then(function(text) { - post("nodes", { + rest_req('POST', "nodes", { name: text }) .then(function(res) { if (res["error"]) { - show_error(res["error"]) + show_info(res["error"]) } else { draw_new_node(text, pos.x, pos.y); } @@ -321,26 +360,22 @@ function create_node() { function create_edge() { if (selected.length < 2) { - show_error("Select at least 2 nodes."); + show_info("Select 2 nodes."); return; } let x = (selected[0].getX() + selected[1].getX()) / 2; let y = (selected[0].getY() + selected[1].getY()) / 2; create_input(x, y).then(function(text) { - post("edges", { + rest_req('POST',"edges", { start: selected[0].id(), end: selected[1].id(), weight: text }) .then(function(res) { if (res["error"]) { - show_error(res["error"]) + show_info(res["error"]) } else { - draw_new_edge({ - "start": selected[0].children[1].text(), - "end": selected[1].children[1].text(), - "weight": text - }); + draw_new_edge(res); selected = []; draw_selected_nodes(); } @@ -350,56 +385,51 @@ function create_edge() { }, function() {}); } -function create_input(x, y) { - return new Promise(function(resolve, reject) { - var container = document.createElement("div"); - container.classList.add("background"); - var input = document.createElement("input"); - var clear = function() { - container.remove(); - stage.listening(true); - focus = true; - } - input.setAttribute("type", "text"); - input.style.left = x + "px"; - input.style.top = y + "px"; - canvas.appendChild(container); - container.appendChild(input); - input.focus(); - stage.listening(false); - focus = false; - window.addEventListener("keyup", function(event) { - if (event.key === "Escape") { - reject(); - clear(); - } - }); - input.addEventListener("keyup", function(event) { - if (event.key === "Enter") { - resolve(input.value); - clear(); - } - }); - }); -} function find_path(data) { if (data.length < 2) { - show_error("Select at least 2 nodes."); + show_info("Select 2 nodes."); return; } - fetch(url + "paths/" + data[0].attrs.id + "/" + data[1].attrs.id).then(res => res.json()) + rest_req("GET", "paths/" + data[0].attrs.id + "/" + data[1].attrs.id, null) .then(function(res) { if (res["error"]) { - show_error(res["error"]); + show_info(res["error"]); } else { selected = []; draw_path(res["path"]); + show_info(res["path"], "#infobar"); } }) .catch(error => console.error('Error:', error)); } +//----------------------------------------------------------------------------- + +function save_graph() { + fetch(url+"save").then(res => res.json()) + .then(function(res) { + if (res["error"]) { + show_info(res["error"]); + } else { + console.log(res); + show_info(res["info"], "#infobar"); + } + }) + .catch(error => console.error('Error:', error)); +} + +function clear_graph() { + fetch(url+"clear").then(res => res.json()) + .then(function(res) { + if (res["error"]) { + show_info(res["error"]); + } else { + location.reload(); + } + }) + .catch(error => console.error('Error:', error)); +} //----------------------------------------------------------------------------- //------------------------background methods----------------------------------- @@ -507,8 +537,9 @@ var anim = new Konva.Animation(function(frame) { }, node_layer); anim.start(); -initialization(); // bug: this avoids glitches window.onfocus = function() {focus = true;}; window.onblur = function() {focus = false;}; + +initialization(); diff --git a/frontend/index.html b/frontend/index.html index 60153cf..784783c 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -10,7 +10,7 @@
-
You have to select at least 2 nodes
+
You have to select at least 2 nodes