v9.18: restore graph button, add text wrapping in SVG, fix scrolling

This commit is contained in:
Naeel
2026-04-16 17:56:38 +03:00
parent a870903c8d
commit 4dfbea90c8
2 changed files with 60 additions and 54 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ spec:
spec: spec:
containers: containers:
- name: cloud-dashboard - name: cloud-dashboard
image: naeel/cloud-dashboard:v9.17 image: naeel/cloud-dashboard:v9.18
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:
- containerPort: 8080 - containerPort: 8080
+59 -53
View File
@@ -39,6 +39,8 @@
.btn-graph:hover { background: #2563eb; } .btn-graph:hover { background: #2563eb; }
.btn-theme { padding: 5px 10px; background: #21262d; border: 1px solid #30363d; border-radius: 6px; color: #e1e4e8; cursor: pointer; font-size: 14px; line-height: 1; } .btn-theme { padding: 5px 10px; background: #21262d; border: 1px solid #30363d; border-radius: 6px; color: #e1e4e8; cursor: pointer; font-size: 14px; line-height: 1; }
.btn-theme:hover { background: #30363d; } .btn-theme:hover { background: #30363d; }
.btn-graph { padding: 5px 12px; background: #21262d; border: 1px solid #30363d; border-radius: 6px; color: #e1e4e8; cursor: pointer; font-size: 12px; }
.btn-graph:hover { background: #30363d; }
.header-user { display: flex; align-items: center; gap: 10px; } .header-user { display: flex; align-items: center; gap: 10px; }
.user-email { font-size: 12px; color: #8b949e; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .user-email { font-size: 12px; color: #8b949e; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.btn-exit { padding: 5px 12px; background: #21262d; border: 1px solid #30363d; border-radius: 6px; color: #f85149; cursor: pointer; font-size: 12px; } .btn-exit { padding: 5px 12px; background: #21262d; border: 1px solid #30363d; border-radius: 6px; color: #f85149; cursor: pointer; font-size: 12px; }
@@ -133,8 +135,8 @@
body.light header { background: #ffffff; border-bottom-color: #cbd5e1; } body.light header { background: #ffffff; border-bottom-color: #cbd5e1; }
body.light .header-logo .logo-text { color: #0f172a; } body.light .header-logo .logo-text { color: #0f172a; }
body.light .header-logo .logo-sub, body.light .user-email { color: #475569; } body.light .header-logo .logo-sub, body.light .user-email { color: #475569; }
body.light .btn-refresh, body.light .btn-theme, body.light .btn-exit { background: #f8fafc; border-color: #cbd5e1; color: #0f172a; } body.light .btn-refresh, body.light .btn-theme, body.light .btn-exit, body.light .btn-graph { background: #f8fafc; border-color: #cbd5e1; color: #0f172a; }
body.light .btn-refresh:hover, body.light .btn-theme:hover, body.light .btn-exit:hover { background: #e2e8f0; } body.light .btn-refresh:hover, body.light .btn-theme:hover, body.light .btn-exit:hover, body.light .btn-graph:hover { background: #e2e8f0; }
body.light .main { background: #f3f6fb; } body.light .main { background: #f3f6fb; }
body.light table { background: #ffffff; border-color: #cbd5e1; } body.light table { background: #ffffff; border-color: #cbd5e1; }
body.light thead { background: #e2e8f0; } body.light thead { background: #e2e8f0; }
@@ -205,7 +207,7 @@
</div> </div>
<div class="spacer"></div> <div class="spacer"></div>
<button class="btn-refresh" id="btn-refresh">&#8635; Обновить</button> <button class="btn-refresh" id="btn-refresh">&#8635; Обновить</button>
<button class="btn-graph" id="btn-running-graph">📊 Граф зависимостей</button>
<button class="btn-theme" id="btn-theme" title="Сменить тему">🌙</button> <button class="btn-theme" id="btn-theme" title="Сменить тему">🌙</button>
<div class="header-user"> <div class="header-user">
<span class="user-email" id="user-email"></span> <span class="user-email" id="user-email"></span>
@@ -540,7 +542,7 @@ function drawGraph(containerId, data, focusUid, forceShowAux){
graphDrawing = false; graphDrawing = false;
return; return;
} }
var layoutMeta = buildRowLayout(data, el.clientWidth || 1200, forceShowAux); var layoutMeta = buildRowLayout(data, 1200, forceShowAux);
var posMap = layoutMeta.positions; var posMap = layoutMeta.positions;
var visibleNodes = data.nodes || []; var visibleNodes = data.nodes || [];
@@ -563,20 +565,25 @@ function drawGraph(containerId, data, focusUid, forceShowAux){
var offsetX = -minX + padding; var offsetX = -minX + padding;
var offsetY = -minY + padding; var offsetY = -minY + padding;
// Create SVG container for scrolling // Create scrollable container
var scrollContainer = document.createElement("div");
scrollContainer.style.width = "100%";
scrollContainer.style.height = "100%";
scrollContainer.style.overflow = "auto";
scrollContainer.style.backgroundColor = "#0d1117";
// Create SVG
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", containerW); svg.setAttribute("width", containerW);
svg.setAttribute("height", containerH); svg.setAttribute("height", containerH);
svg.setAttribute("viewBox", "0 0 " + containerW + " " + containerH); svg.setAttribute("viewBox", "0 0 " + containerW + " " + containerH);
svg.style.display = "block"; svg.style.display = "block";
svg.style.width = "100%"; svg.style.minWidth = containerW + "px";
svg.style.height = "100%"; svg.style.minHeight = containerH + "px";
svg.style.backgroundColor = "#0d1117";
svg.style.cursor = "default";
var g = document.createElementNS("http://www.w3.org/2000/svg", "g"); var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
// Draw nodes // Draw nodes with text wrapping via tspan
visibleNodes.forEach(function(n){ visibleNodes.forEach(function(n){
var pos = posMap[n.id]; var pos = posMap[n.id];
if(!pos) return; if(!pos) return;
@@ -604,60 +611,54 @@ function drawGraph(containerId, data, focusUid, forceShowAux){
}); });
g.appendChild(rect); g.appendChild(rect);
// Text: name (bold, underline), svc (italic), age (small) // Text with wrapping: name + svc + age
var nameFs = Math.round((n.fontSize || 13) * 1.25); var nameFs = Math.round((n.fontSize || 13) * 1.25);
var txtX = x; var txtX = x;
var txtY = y - h/4; var txtY = y - h/4;
var lineH = nameFs * 1.5;
var lines = [];
// Split name into lines (rough estimate)
var charsPerLine = Math.max(8, Math.floor((w - 20) / (nameFs * 0.6)));
var nameLines = [];
for(var i = 0; i < n.label.length; i += charsPerLine){
nameLines.push(n.label.substring(i, i + charsPerLine));
}
nameLines.forEach(function(line){ lines.push({text: line, size: nameFs, weight: "bold", style: "normal", color: "#dbeafe"}); });
// Name
var nameText = document.createElementNS("http://www.w3.org/2000/svg", "text");
nameText.setAttribute("x", txtX);
nameText.setAttribute("y", txtY);
nameText.setAttribute("text-anchor", "middle");
nameText.setAttribute("font-size", nameFs);
nameText.setAttribute("font-weight", "bold");
nameText.setAttribute("text-decoration", "underline");
nameText.setAttribute("fill", "#dbeafe");
nameText.setAttribute("font-family", "system-ui, sans-serif");
nameText.textContent = n.label || n.id || "";
nameText.style.pointerEvents = "none";
g.appendChild(nameText);
// Service
if(n.svc){ if(n.svc){
var svcText = document.createElementNS("http://www.w3.org/2000/svg", "text"); lines.push({text: n.svc, size: nameFs - 2, weight: "normal", style: "italic", color: "#93c5fd"});
svcText.setAttribute("x", txtX); }
svcText.setAttribute("y", txtY + nameFs * 0.8 + 3);
svcText.setAttribute("text-anchor", "middle"); if(n.age){
svcText.setAttribute("font-size", (nameFs - 2)); lines.push({text: "⏱ " + n.age, size: 9, weight: "normal", style: "italic", color: "#9ca3af"});
svcText.setAttribute("font-style", "italic");
svcText.setAttribute("fill", "#93c5fd");
svcText.setAttribute("font-family", "system-ui, sans-serif");
svcText.textContent = n.svc;
svcText.style.pointerEvents = "none";
g.appendChild(svcText);
} }
// Age // Render text lines
if(n.age){ var textG = document.createElementNS("http://www.w3.org/2000/svg", "g");
var ageText = document.createElementNS("http://www.w3.org/2000/svg", "text"); var currentY = txtY - (lines.length * lineH / 2);
ageText.setAttribute("x", txtX); lines.forEach(function(line){
ageText.setAttribute("y", y + h/4 + 4); var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
ageText.setAttribute("text-anchor", "middle"); text.setAttribute("x", txtX);
ageText.setAttribute("font-size", 9); text.setAttribute("y", currentY);
ageText.setAttribute("font-style", "italic"); text.setAttribute("text-anchor", "middle");
ageText.setAttribute("fill", "#9ca3af"); text.setAttribute("font-size", line.size);
ageText.setAttribute("font-family", "system-ui, sans-serif"); text.setAttribute("font-weight", line.weight);
ageText.textContent = "⏱ " + n.age; text.setAttribute("font-style", line.style);
ageText.style.pointerEvents = "none"; text.setAttribute("fill", line.color);
g.appendChild(ageText); text.setAttribute("font-family", "system-ui, sans-serif");
} text.textContent = line.text;
text.style.pointerEvents = "none";
textG.appendChild(text);
currentY += lineH;
});
g.appendChild(textG);
}); });
svg.appendChild(g); svg.appendChild(g);
el.appendChild(svg); scrollContainer.appendChild(svg);
el.appendChild(scrollContainer);
// Auto-fit on container size change
setTimeout(function(){ graphDrawing = false; }, 50); setTimeout(function(){ graphDrawing = false; }, 50);
} }
@@ -1272,9 +1273,14 @@ function initApp(){
document.getElementById("btn-login").addEventListener("click", doLogin); document.getElementById("btn-login").addEventListener("click", doLogin);
document.getElementById("btn-refresh").addEventListener("click", loadAllDetails); document.getElementById("btn-refresh").addEventListener("click", loadAllDetails);
document.getElementById("btn-running-graph").addEventListener("click", openRunningGraph);
document.getElementById("btn-theme").addEventListener("click", toggleTheme); document.getElementById("btn-theme").addEventListener("click", toggleTheme);
document.getElementById("btn-exit").addEventListener("click", doLogout); document.getElementById("btn-exit").addEventListener("click", doLogout);
document.getElementById("graph-modal").addEventListener("click", function(e){
if(e.target && e.target.id === "graph-modal") closeRunningGraph();
});
applyTheme(localStorage.getItem("dashboard_theme") || "dark"); applyTheme(localStorage.getItem("dashboard_theme") || "dark");
var envSelect = document.getElementById("deck-env"); var envSelect = document.getElementById("deck-env");