From 47dd66e5e7d85f727b0d5da3c99fdd863058e723 Mon Sep 17 00:00:00 2001 From: Matiss Jurevics Date: Mon, 15 Dec 2025 21:28:49 +0000 Subject: [PATCH] feat: implement custom interactive tooltips for ActivityHeatmap bars to display detailed activity data on hover. --- src/components/ActivityHeatmap.jsx | 58 +++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/components/ActivityHeatmap.jsx b/src/components/ActivityHeatmap.jsx index 69f6923..d987b64 100644 --- a/src/components/ActivityHeatmap.jsx +++ b/src/components/ActivityHeatmap.jsx @@ -6,6 +6,7 @@ const ActivityHeatmap = () => { const [loading, setLoading] = useState(true); const [data, setData] = useState([]); const [error, setError] = useState(null); + const [hoveredData, setHoveredData] = useState(null); useEffect(() => { const fetchData = async () => { @@ -181,18 +182,18 @@ const ActivityHeatmap = () => { // Draw Gitea Bar (Bottom) if (d.gitea > 0) { - drawBar(g, pos.x, pos.y, tileWidth, tileHeight, giteaH, colorGitea, `Gitea: ${d.gitea} on ${d.date.toDateString()}`); + drawBar(g, pos.x, pos.y, tileWidth, tileHeight, giteaH, colorGitea, `Gitea: ${d.gitea} on ${d.date.toDateString()}`, d); } // Draw GitHub Bar (Top) // Adjust y position up by gitea height if (d.github > 0) { - drawBar(g, pos.x, pos.y - giteaH, tileWidth, tileHeight, githubH, colorGithub, `GitHub: ${d.github} on ${d.date.toDateString()}`); + drawBar(g, pos.x, pos.y - giteaH, tileWidth, tileHeight, githubH, colorGithub, `GitHub: ${d.github} on ${d.date.toDateString()}`, d); } }); // Function to draw isometric prism - function drawBar(container, x, y, w, h, z, color, tooltipText) { + function drawBar(container, x, y, w, h, z, color, tooltipText, dataItem) { // Top Face const pathTop = `M${x} ${y - z} L${x + w} ${y + h - z} @@ -226,11 +227,24 @@ const ActivityHeatmap = () => { group.append("title").text(tooltipText); // Hover effect - // group.on("mouseenter", function() { - // d3.select(this).selectAll("path").attr("opacity", 0.8); - // }).on("mouseleave", function() { - // d3.select(this).selectAll("path").attr("opacity", 1); - // }); + group.on("mouseenter", function (event) { + d3.select(this).selectAll("path").attr("opacity", 0.8); + // Calculate position relative to container + const [mx, my] = d3.pointer(event, svg.node()); + setHoveredData({ + x: mx, + y: my, + date: d3.select(this).datum().date, + github: d3.select(this).datum().github, + gitea: d3.select(this).datum().gitea + }); + }).on("mouseleave", function () { + d3.select(this).selectAll("path").attr("opacity", 1); + setHoveredData(null); + }); + + // Attach data to group for access in handler + group.datum(dataItem); } }, [data, loading]); @@ -262,7 +276,8 @@ const ActivityHeatmap = () => { alignItems: 'center', justifyContent: 'center', padding: '0', - overflow: 'hidden' + overflow: 'visible', // Allow tooltip to render outside if needed + position: 'relative' // Anchor for absolute tooltip }}>

Contribution Topography @@ -282,6 +297,31 @@ const ActivityHeatmap = () => { preserveAspectRatio="xMidYMid meet" style={{ width: '100%', height: 'auto', overflow: 'visible' }} /> + + {hoveredData && ( +
+
+ {hoveredData.date ? hoveredData.date.toDateString() : 'Date'} +
+
+
GitHub: {hoveredData.github || 0}
+
Gitea: {hoveredData.gitea || 0}
+
+
+ )} ); };