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:
containers:
- name: cloud-dashboard
image: naeel/cloud-dashboard:v9.17
image: naeel/cloud-dashboard:v9.18
imagePullPolicy: Always
ports:
- containerPort: 8080
+59 -53
View File
@@ -39,6 +39,8 @@
.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: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; }
.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; }
@@ -133,8 +135,8 @@
body.light header { background: #ffffff; border-bottom-color: #cbd5e1; }
body.light .header-logo .logo-text { color: #0f172a; }
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:hover, body.light .btn-theme:hover, body.light .btn-exit:hover { background: #e2e8f0; }
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, body.light .btn-graph:hover { background: #e2e8f0; }
body.light .main { background: #f3f6fb; }
body.light table { background: #ffffff; border-color: #cbd5e1; }
body.light thead { background: #e2e8f0; }
@@ -205,7 +207,7 @@
</div>
<div class="spacer"></div>
<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>
<div class="header-user">
<span class="user-email" id="user-email"></span>
@@ -540,7 +542,7 @@ function drawGraph(containerId, data, focusUid, forceShowAux){
graphDrawing = false;
return;
}
var layoutMeta = buildRowLayout(data, el.clientWidth || 1200, forceShowAux);
var layoutMeta = buildRowLayout(data, 1200, forceShowAux);
var posMap = layoutMeta.positions;
var visibleNodes = data.nodes || [];
@@ -563,20 +565,25 @@ function drawGraph(containerId, data, focusUid, forceShowAux){
var offsetX = -minX + 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");
svg.setAttribute("width", containerW);
svg.setAttribute("height", containerH);
svg.setAttribute("viewBox", "0 0 " + containerW + " " + containerH);
svg.style.display = "block";
svg.style.width = "100%";
svg.style.height = "100%";
svg.style.backgroundColor = "#0d1117";
svg.style.cursor = "default";
svg.style.minWidth = containerW + "px";
svg.style.minHeight = containerH + "px";
var g = document.createElementNS("http://www.w3.org/2000/svg", "g");
// Draw nodes
// Draw nodes with text wrapping via tspan
visibleNodes.forEach(function(n){
var pos = posMap[n.id];
if(!pos) return;
@@ -604,60 +611,54 @@ function drawGraph(containerId, data, focusUid, forceShowAux){
});
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 txtX = x;
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){
var svcText = document.createElementNS("http://www.w3.org/2000/svg", "text");
svcText.setAttribute("x", txtX);
svcText.setAttribute("y", txtY + nameFs * 0.8 + 3);
svcText.setAttribute("text-anchor", "middle");
svcText.setAttribute("font-size", (nameFs - 2));
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);
lines.push({text: n.svc, size: nameFs - 2, weight: "normal", style: "italic", color: "#93c5fd"});
}
if(n.age){
lines.push({text: "⏱ " + n.age, size: 9, weight: "normal", style: "italic", color: "#9ca3af"});
}
// Age
if(n.age){
var ageText = document.createElementNS("http://www.w3.org/2000/svg", "text");
ageText.setAttribute("x", txtX);
ageText.setAttribute("y", y + h/4 + 4);
ageText.setAttribute("text-anchor", "middle");
ageText.setAttribute("font-size", 9);
ageText.setAttribute("font-style", "italic");
ageText.setAttribute("fill", "#9ca3af");
ageText.setAttribute("font-family", "system-ui, sans-serif");
ageText.textContent = "⏱ " + n.age;
ageText.style.pointerEvents = "none";
g.appendChild(ageText);
}
// Render text lines
var textG = document.createElementNS("http://www.w3.org/2000/svg", "g");
var currentY = txtY - (lines.length * lineH / 2);
lines.forEach(function(line){
var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", txtX);
text.setAttribute("y", currentY);
text.setAttribute("text-anchor", "middle");
text.setAttribute("font-size", line.size);
text.setAttribute("font-weight", line.weight);
text.setAttribute("font-style", line.style);
text.setAttribute("fill", line.color);
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);
el.appendChild(svg);
scrollContainer.appendChild(svg);
el.appendChild(scrollContainer);
// Auto-fit on container size change
setTimeout(function(){ graphDrawing = false; }, 50);
}
@@ -1272,9 +1273,14 @@ function initApp(){
document.getElementById("btn-login").addEventListener("click", doLogin);
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-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");
var envSelect = document.getElementById("deck-env");