diff --git a/dist/html-static/css/resizeable.css b/dist/html-static/css/resizeable.css new file mode 100644 index 00000000..d96ca42d --- /dev/null +++ b/dist/html-static/css/resizeable.css @@ -0,0 +1,69 @@ + +#panel-vpn-service { + top: 85px; + right: 40px; + + /* height: 400px; */ + width: 340px; + + max-height: 85vh; /* 80% of the viewport height */ + max-width: 80vw; /* 80% of the viewport width */ + + /* min-height: 400px; */ + min-width: 340px; +} + +.resizable { + /* box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.4); */ + /* background-color: rgba(140, 140, 140, 1); */ + position: absolute; +} + +.resizable .resizers{ + width: 100%; + height: 100%; + box-sizing: border-box; +} + +.resizable .resizers .resizer{ + width: 10px; + height: 10px; + border-radius: 50%; /*magic to turn square into circle*/ + position: absolute; +} + +.resizable .resizers .resizer.top-left { + left: -5px; + top: -5px; + cursor: nwse-resize; /*resizer cursor*/ + background-color: rgba(140, 140, 140, 0.5); +} +.resizable .resizers .resizer.top-right { + right: -5px; + top: -5px; + cursor: nesw-resize; + background-color: rgba(140, 140, 140, 0.5); +} +.resizable .resizers .resizer.bottom-left { + left: -5px; + bottom: 5px; + cursor: nesw-resize; + background-color: rgba(140, 140, 140, 0.5); +} +.resizable .resizers .resizer.bottom-right { + right: -5px; + bottom: -5px; + cursor: nwse-resize; + background-color: rgba(140, 140, 140, 0.5); +} + +.resizable .resizers .resizer.mid-left { + + /* left: -5px; + bottom: 5px; */ + top: 40px; + cursor: ew-resize; + + background-color: rgba(140, 140, 140, 0.5); + +} \ No newline at end of file diff --git a/dist/html-static/js/dev.js b/dist/html-static/js/dev.js index f55ca22d..e142e6d6 100644 --- a/dist/html-static/js/dev.js +++ b/dist/html-static/js/dev.js @@ -660,6 +660,10 @@ document.addEventListener("DOMContentLoaded", async function() { topoViewerNode = cy.filter('node[name = "topoviewer"]'); topoViewerNode.remove(); + // remove node TopoViewerParentNode + topoViewerParentNode = cy.filter('node[name = "TopoViewer:1"]'); + topoViewerParentNode.remove(); + var cyExpandCollapse = cy.expandCollapse({ layoutBy: null, // null means use existing layout undoable: false, diff --git a/dist/html-static/js/library/bulma-resizeable-panel.min.js b/dist/html-static/js/library/bulma-resizeable-panel.min.js new file mode 100644 index 00000000..c4615686 --- /dev/null +++ b/dist/html-static/js/library/bulma-resizeable-panel.min.js @@ -0,0 +1,170 @@ + /* Make resizable div by Hung Nguyen */ + function makeResizableDiv(div) { + const element = document.querySelector(div); + const resizers = document.querySelectorAll(div + ' .resizer'); + // const minimum_size = 20; + + var panelNode = document.getElementById("panel-vpn-service"); + + console.log("panelNode: ", panelNode); + + const rect = panelNode.getBoundingClientRect(); + const minimum_size_width = rect.width; + const minimum_size_height = rect.height; + + console.log("Rendered Width: ", minimum_size_width); + console.log("Rendered Height: ", minimum_size_height); + + const minimum_size = 20 + + let original_width = 0; + let original_height = 0; + let original_x = 0; + let original_y = 0; + let original_mouse_x = 0; + let original_mouse_y = 0; + + for (let i = 0; i < resizers.length; i++) { + const currentResizer = resizers[i]; + currentResizer.addEventListener('mousedown', function(e) { + e.preventDefault(); + original_width = parseFloat( + getComputedStyle(element, null).getPropertyValue('width').replace('px', '') + ); + original_height = parseFloat( + getComputedStyle(element, null).getPropertyValue('height').replace('px', '') + ); + original_x = element.getBoundingClientRect().left; + original_y = element.getBoundingClientRect().top; + original_mouse_x = e.pageX; + original_mouse_y = e.pageY; + window.addEventListener('mousemove', resize); + window.addEventListener('mouseup', stopResize); + }); + + function resize(e) { + if (currentResizer.classList.contains('bottom-right')) { + const width = original_width + (e.pageX - original_mouse_x); + const height = original_height + (e.pageY - original_mouse_y); + if (width > minimum_size) { + element.style.width = width + 'px'; + } + if (height > minimum_size) { + element.style.height = height + 'px'; + } + + } else if (currentResizer.classList.contains('mid-left')) { + const height = original_height + (e.pageY - original_mouse_y); + const width = original_width - (e.pageX - original_mouse_x); + if (width > minimum_size_width) { + element.style.width = width + 'px'; + // element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'; + console.log("left: ", element.style.left); + } + + + } else if (currentResizer.classList.contains('bottom-left')) { + const height = original_height + (e.pageY - original_mouse_y); + const width = original_width - (e.pageX - original_mouse_x); + if (height > minimum_size_height) { + element.style.height = height + 'px'; + console.log("height: ", element.style.height); + } + if (width > minimum_size_width) { + element.style.width = width + 'px'; + element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'; + console.log("left: ", element.style.left); + } + + + + } else if (currentResizer.classList.contains('top-right')) { + const width = original_width + (e.pageX - original_mouse_x); + const height = original_height - (e.pageY - original_mouse_y); + if (width > minimum_size) { + element.style.width = width + 'px'; + } + if (height > minimum_size) { + element.style.height = height + 'px'; + element.style.top = original_y + (e.pageY - original_mouse_y) + 'px'; + } + } else { + const width = original_width - (e.pageX - original_mouse_x); + const height = original_height - (e.pageY - original_mouse_y); + if (width > minimum_size) { + element.style.width = width + 'px'; + element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'; + } + if (height > minimum_size) { + element.style.height = height + 'px'; + element.style.top = original_y + (e.pageY - original_mouse_y) + 'px'; + } + } + } + + function stopResize() { + window.removeEventListener('mousemove', resize); + } + } + } + + makeResizableDiv('.resizable'); + + + // tabulator + // Sample data + const activeSymptoms = Array.from({ + length: 120 + }, (_, i) => ({ + rootCause: `eBGP Session to neighbor 30.1.1.${i + 2} is not up for Device: Node-${i + 5}, Vrf: vpn-${i + 100}`, + subservice: "subservice.ebgp.nbr.health system", + priority: Math.floor(Math.random() * 256), + lastUpdated: `19-Jan-2023 ${("0" + (i % 12 + 1)).slice(-2)}:${("0" + (i % 60)).slice(-2)}:34 AM`, + })); + + // Tabulator Column Definitions + const columns = [{ + title: "Root Cause", + field: "rootCause", + widthGrow: 3, + headerFilter: "input" + }, + { + title: "Subservice", + field: "subservice", + widthGrow: 2, + headerFilter: "input" + }, + { + title: "Priority", + field: "priority", + widthGrow: 1, + hozAlign: "right", + sorter: "number", + headerFilter: "number" + }, + { + title: "Last Updated", + field: "lastUpdated", + widthGrow: 1, + sorter: "datetime", + headerFilter: "input" + }, + ]; + + // Initialize Tabulator + new Tabulator("#tabulator-grid", { + data: activeSymptoms, + layout: "fitData", + // responsiveLayout: true, + tooltips: true, + pagination: "local", + paginationSize: 5, + movableColumns: true, + resizableRows: true, + initialSort: [{ + column: "priority", + dir: "desc" + }], + columns, + }); \ No newline at end of file diff --git a/dist/topoviewer b/dist/topoviewer index e9b6fd2c..14a4173a 100755 Binary files a/dist/topoviewer and b/dist/topoviewer differ diff --git a/go_cloudshellwrapper/constants.go b/go_cloudshellwrapper/constants.go index 8c277aac..61a55e2a 100755 --- a/go_cloudshellwrapper/constants.go +++ b/go_cloudshellwrapper/constants.go @@ -1,6 +1,6 @@ package cloudshellwrapper -var VersionInfo string = "nightly-25.01.01" +var VersionInfo string = "nightly-25.01.02" // create html-public files var HtmlPublicPrefixPath string = "./html-public/" diff --git a/go_cloudshellwrapper/constants.go.bak b/go_cloudshellwrapper/constants.go.bak index cd6ec464..8c277aac 100755 --- a/go_cloudshellwrapper/constants.go.bak +++ b/go_cloudshellwrapper/constants.go.bak @@ -1,6 +1,6 @@ package cloudshellwrapper -var VersionInfo string = "nightly-24.12.31" +var VersionInfo string = "nightly-25.01.01" // create html-public files var HtmlPublicPrefixPath string = "./html-public/" diff --git a/go_topoengine/adaptorClab.go b/go_topoengine/adaptorClab.go index e5c2cf30..5d3f899e 100755 --- a/go_topoengine/adaptorClab.go +++ b/go_topoengine/adaptorClab.go @@ -221,6 +221,12 @@ func (cyTopo *CytoTopology) UnmarshalContainerLabTopoV2(topoFile []byte, clabHos cytoJson.Data.Weight = "30" cytoJson.Data.Name = node.ID cytoJson.Data.TopoViewerRole = node.Labels.TopoViewerRole + if len(node.Labels.TopoViewerRole) != 0 { + cytoJson.Data.TopoViewerRole = node.Labels.TopoViewerRole + } else { + // defaulting to "PE" + cytoJson.Data.TopoViewerRole = "pe" + } // if node.ID == "topoviewer" { // cytoJson.Data.Lat = "51.45664108633426" // cytoJson.Data.Lng = "7.00441511803141" @@ -239,6 +245,9 @@ func (cyTopo *CytoTopology) UnmarshalContainerLabTopoV2(topoFile []byte, clabHos if len(node.Labels.TopoViewerGroup) != 0 && len(node.Labels.TopoViewerGroupLevel) != 0 { // combine to be "Data Center Spine:1" cytoJson.Data.Parent = node.Labels.TopoViewerGroup + ":" + node.Labels.TopoViewerGroupLevel + } else { + // defaulting to "topoviewer:1" + cytoJson.Data.Parent = "topoviewer:1" } // create list of parent nodes @@ -346,6 +355,7 @@ func (cyTopo *CytoTopology) UnmarshalContainerLabTopoV2(topoFile []byte, clabHos cytoJson.Data.ID = n cytoJson.Data.Name = strings.Split(n, ":")[0] cytoJson.Data.TopoViewerRole = "group" + cytoJson.Data.Weight = "1000" // defaulting to the same lat and lng as the last-node // cytoJson.Data.Lat = "51.45664108633426" diff --git a/html-static/css/resizeable.css b/html-static/css/resizeable.css new file mode 100644 index 00000000..d96ca42d --- /dev/null +++ b/html-static/css/resizeable.css @@ -0,0 +1,69 @@ + +#panel-vpn-service { + top: 85px; + right: 40px; + + /* height: 400px; */ + width: 340px; + + max-height: 85vh; /* 80% of the viewport height */ + max-width: 80vw; /* 80% of the viewport width */ + + /* min-height: 400px; */ + min-width: 340px; +} + +.resizable { + /* box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.4); */ + /* background-color: rgba(140, 140, 140, 1); */ + position: absolute; +} + +.resizable .resizers{ + width: 100%; + height: 100%; + box-sizing: border-box; +} + +.resizable .resizers .resizer{ + width: 10px; + height: 10px; + border-radius: 50%; /*magic to turn square into circle*/ + position: absolute; +} + +.resizable .resizers .resizer.top-left { + left: -5px; + top: -5px; + cursor: nwse-resize; /*resizer cursor*/ + background-color: rgba(140, 140, 140, 0.5); +} +.resizable .resizers .resizer.top-right { + right: -5px; + top: -5px; + cursor: nesw-resize; + background-color: rgba(140, 140, 140, 0.5); +} +.resizable .resizers .resizer.bottom-left { + left: -5px; + bottom: 5px; + cursor: nesw-resize; + background-color: rgba(140, 140, 140, 0.5); +} +.resizable .resizers .resizer.bottom-right { + right: -5px; + bottom: -5px; + cursor: nwse-resize; + background-color: rgba(140, 140, 140, 0.5); +} + +.resizable .resizers .resizer.mid-left { + + /* left: -5px; + bottom: 5px; */ + top: 40px; + cursor: ew-resize; + + background-color: rgba(140, 140, 140, 0.5); + +} \ No newline at end of file diff --git a/html-static/js/dev.js b/html-static/js/dev.js index f55ca22d..e142e6d6 100644 --- a/html-static/js/dev.js +++ b/html-static/js/dev.js @@ -660,6 +660,10 @@ document.addEventListener("DOMContentLoaded", async function() { topoViewerNode = cy.filter('node[name = "topoviewer"]'); topoViewerNode.remove(); + // remove node TopoViewerParentNode + topoViewerParentNode = cy.filter('node[name = "TopoViewer:1"]'); + topoViewerParentNode.remove(); + var cyExpandCollapse = cy.expandCollapse({ layoutBy: null, // null means use existing layout undoable: false, diff --git a/html-static/js/library/bulma-resizeable-panel.min.js b/html-static/js/library/bulma-resizeable-panel.min.js new file mode 100644 index 00000000..c4615686 --- /dev/null +++ b/html-static/js/library/bulma-resizeable-panel.min.js @@ -0,0 +1,170 @@ + /* Make resizable div by Hung Nguyen */ + function makeResizableDiv(div) { + const element = document.querySelector(div); + const resizers = document.querySelectorAll(div + ' .resizer'); + // const minimum_size = 20; + + var panelNode = document.getElementById("panel-vpn-service"); + + console.log("panelNode: ", panelNode); + + const rect = panelNode.getBoundingClientRect(); + const minimum_size_width = rect.width; + const minimum_size_height = rect.height; + + console.log("Rendered Width: ", minimum_size_width); + console.log("Rendered Height: ", minimum_size_height); + + const minimum_size = 20 + + let original_width = 0; + let original_height = 0; + let original_x = 0; + let original_y = 0; + let original_mouse_x = 0; + let original_mouse_y = 0; + + for (let i = 0; i < resizers.length; i++) { + const currentResizer = resizers[i]; + currentResizer.addEventListener('mousedown', function(e) { + e.preventDefault(); + original_width = parseFloat( + getComputedStyle(element, null).getPropertyValue('width').replace('px', '') + ); + original_height = parseFloat( + getComputedStyle(element, null).getPropertyValue('height').replace('px', '') + ); + original_x = element.getBoundingClientRect().left; + original_y = element.getBoundingClientRect().top; + original_mouse_x = e.pageX; + original_mouse_y = e.pageY; + window.addEventListener('mousemove', resize); + window.addEventListener('mouseup', stopResize); + }); + + function resize(e) { + if (currentResizer.classList.contains('bottom-right')) { + const width = original_width + (e.pageX - original_mouse_x); + const height = original_height + (e.pageY - original_mouse_y); + if (width > minimum_size) { + element.style.width = width + 'px'; + } + if (height > minimum_size) { + element.style.height = height + 'px'; + } + + } else if (currentResizer.classList.contains('mid-left')) { + const height = original_height + (e.pageY - original_mouse_y); + const width = original_width - (e.pageX - original_mouse_x); + if (width > minimum_size_width) { + element.style.width = width + 'px'; + // element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'; + console.log("left: ", element.style.left); + } + + + } else if (currentResizer.classList.contains('bottom-left')) { + const height = original_height + (e.pageY - original_mouse_y); + const width = original_width - (e.pageX - original_mouse_x); + if (height > minimum_size_height) { + element.style.height = height + 'px'; + console.log("height: ", element.style.height); + } + if (width > minimum_size_width) { + element.style.width = width + 'px'; + element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'; + console.log("left: ", element.style.left); + } + + + + } else if (currentResizer.classList.contains('top-right')) { + const width = original_width + (e.pageX - original_mouse_x); + const height = original_height - (e.pageY - original_mouse_y); + if (width > minimum_size) { + element.style.width = width + 'px'; + } + if (height > minimum_size) { + element.style.height = height + 'px'; + element.style.top = original_y + (e.pageY - original_mouse_y) + 'px'; + } + } else { + const width = original_width - (e.pageX - original_mouse_x); + const height = original_height - (e.pageY - original_mouse_y); + if (width > minimum_size) { + element.style.width = width + 'px'; + element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'; + } + if (height > minimum_size) { + element.style.height = height + 'px'; + element.style.top = original_y + (e.pageY - original_mouse_y) + 'px'; + } + } + } + + function stopResize() { + window.removeEventListener('mousemove', resize); + } + } + } + + makeResizableDiv('.resizable'); + + + // tabulator + // Sample data + const activeSymptoms = Array.from({ + length: 120 + }, (_, i) => ({ + rootCause: `eBGP Session to neighbor 30.1.1.${i + 2} is not up for Device: Node-${i + 5}, Vrf: vpn-${i + 100}`, + subservice: "subservice.ebgp.nbr.health system", + priority: Math.floor(Math.random() * 256), + lastUpdated: `19-Jan-2023 ${("0" + (i % 12 + 1)).slice(-2)}:${("0" + (i % 60)).slice(-2)}:34 AM`, + })); + + // Tabulator Column Definitions + const columns = [{ + title: "Root Cause", + field: "rootCause", + widthGrow: 3, + headerFilter: "input" + }, + { + title: "Subservice", + field: "subservice", + widthGrow: 2, + headerFilter: "input" + }, + { + title: "Priority", + field: "priority", + widthGrow: 1, + hozAlign: "right", + sorter: "number", + headerFilter: "number" + }, + { + title: "Last Updated", + field: "lastUpdated", + widthGrow: 1, + sorter: "datetime", + headerFilter: "input" + }, + ]; + + // Initialize Tabulator + new Tabulator("#tabulator-grid", { + data: activeSymptoms, + layout: "fitData", + // responsiveLayout: true, + tooltips: true, + pagination: "local", + paginationSize: 5, + movableColumns: true, + resizableRows: true, + initialSort: [{ + column: "priority", + dir: "desc" + }], + columns, + }); \ No newline at end of file