diff --git a/code_review_graph/visualization.py b/code_review_graph/visualization.py index c78b5735..ccf90b50 100644 --- a/code_review_graph/visualization.py +++ b/code_review_graph/visualization.py @@ -855,8 +855,11 @@ def generate_html( tooltip.style.left = x + "px"; tooltip.style.top = y + "px"; } function hideTooltip() { tooltip.classList.remove("visible"); } -var W = innerWidth, H = innerHeight; -var svg = d3.select("svg").attr("viewBox", [0, 0, W, H]); +var svgEl = document.getElementById("graph-svg"); +function getW() { return svgEl.clientWidth || window.innerWidth || 800; } +function getH() { return svgEl.clientHeight || window.innerHeight || 600; } +var W = getW(), H = getH(); +var svg = d3.select("#graph-svg").attr("viewBox", [0, 0, W, H]); var gRoot = svg.append("g"); var currentTransform = d3.zoomIdentity; var zoomBehavior = d3.zoom() @@ -1071,13 +1074,18 @@ def generate_html( nodes.forEach(function(n) { if (n.kind === "File") collapsedFiles.add(n.qualified_name); }); } updateNodes(); -function fitGraph() { +function fitGraph(retries) { + if (retries === undefined) retries = 10; var b = gRoot.node().getBBox(); - if (b.width === 0 || b.height === 0) return; + if (b.width === 0 || b.height === 0) { + if (retries > 0) requestAnimationFrame(function() { fitGraph(retries - 1); }); + return; + } var pad = 0.1; var fw = b.width * (1 + 2*pad), fh = b.height * (1 + 2*pad); - var s = Math.min(W / fw, H / fh, 2.5); - var tx = W/2 - (b.x + b.width/2)*s, ty = H/2 - (b.y + b.height/2)*s; + var cw = getW(), ch = getH(); + var s = Math.min(cw / fw, ch / fh, 2.5); + var tx = cw/2 - (b.x + b.width/2)*s, ty = ch/2 - (b.y + b.height/2)*s; svg.transition().duration(600).call(zoomBehavior.transform, d3.zoomIdentity.translate(tx, ty).scale(s)); } var loadingOverlay = document.getElementById("loading-overlay"); @@ -1088,6 +1096,27 @@ def generate_html( } simulation.on("end", function() { loadingOverlay.classList.add("hidden"); + // Re-read dimensions now that the window is fully rendered + var newW = getW(), newH = getH(); + // If initial W/H were wrong, shift all node positions to the correct center + if (Math.abs(newW - W) > 50 || Math.abs(newH - H) > 50 || W < 100 || H < 100) { + var dx = newW/2 - W/2, dy = newH/2 - H/2; + nodes.forEach(function(n) { n.x += dx; n.y += dy; if (n.fx != null) n.fx += dx; if (n.fy != null) n.fy += dy; }); + } + W = newW; H = newH; + svg.attr("viewBox", [0, 0, W, H]); + simulation.force("center", d3.forceCenter(W / 2, H / 2)); + simulation.force("x", d3.forceX(W / 2).strength(0.03)); + simulation.force("y", d3.forceY(H / 2).strength(0.03)); + // Use requestAnimationFrame to guarantee browser has painted before measuring + requestAnimationFrame(function() { fitGraph(); }); +}); +window.addEventListener("resize", function() { + W = getW(); H = getH(); + svg.attr("viewBox", [0, 0, W, H]); + simulation.force("center", d3.forceCenter(W / 2, H / 2)); + simulation.force("x", d3.forceX(W / 2).strength(0.03)); + simulation.force("y", d3.forceY(H / 2).strength(0.03)); fitGraph(); }); function zoomToNode(qn) { @@ -1605,7 +1634,7 @@ def generate_html(
- +