addTooltips = (chart, styles) => {
const stroke_styles = { stroke: "blue", "stroke-width": 3 };
const fill_styles = { fill: "blue", opacity: 0.5 };
const type = d3.select(chart).node().tagName;
let wrapper =
type === "FIGURE" ? d3.select(chart).select("svg") : d3.select(chart);
const svgs = d3.select(chart).selectAll("svg");
if (svgs.size() > 1) wrapper = d3.select([...svgs].pop());
wrapper.style("overflow", "visible");
wrapper.selectAll("path").each(function () {
if (
d3.select(this).attr("fill") === null ||
d3.select(this).attr("fill") === "none"
) {
d3.select(this).style("pointer-events", "visibleStroke");
if (styles === undefined) styles = stroke_styles;
}
});
if (styles === undefined) styles = fill_styles;
const id = id_generator();
d3.select(chart).classed(id, true);
wrapper.selectAll("title").each(function () {
const title = d3.select(this);
const parent = d3.select(this.parentNode); // visual mark on the screen
const t = title.text();
if (t) {
parent.attr("__title", t).classed("has-title", true);
title.remove();
}
// Mouse events
parent
.on("pointerenter pointermove", function (event) {
const text = d3.select(this).attr("__title");
const pointer = d3.pointer(event, d3.select("body").node());
// Create tooltip div and append it to the body if it doesn't already exist
let tip = d3.select("body").select(".tooltip");
if (tip.empty()) {
tip = d3
.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0)
.style("position", "absolute")
.style("min-width", "30px")
.style("height", "auto")
.style("display", "flex")
.style("flex-direction", "column")
.style("align-items", "center")
.style("color", "black")
.style(
"background-image",
"linear-gradient(to bottom, rgba(255, 255, 255, 0.688), rgba(240, 240, 240, 0.688))"
)
.style("padding", "4px 8px")
.style("border-radius", "6px")
.style("border", "1px solid #565656")
.style("box-shadow", "0 1px 4px 0 rgba(0, 0, 0, 0.2)")
.style("pointer-events", "none")
.style("backdrop-filter", "blur(5px)")
.style("font", "0.85rem calibri")
.style(
"transition",
"0.1s opacity ease-out, 0.1s border-color ease-out, 0.2s left ease-out, 0.2s top ease-out"
)
.style("z-index", 9999999);
}
if (text) {
hover(tip, pointer, text.split("\n"));
}
// Raise it
d3.select(this).raise();
})
.on("pointerout", function () {
// Remove the tooltip div from the DOM
d3.select("body").select(".tooltip").remove();
// Lower it!
d3.select(this).lower();
});
});
// Remove the tip if you tap on the wrapper (for mobile)
wrapper.on("touchstart", () => d3.select("body").select(".tooltip").remove());
// Define the styles
const styleElement = document.createElement("style");
styleElement.innerHTML = `
.${id} .has-title { cursor: pointer; pointer-events: all; }
.${id} .has-title:hover { ${Object.entries(styles)
.map(([key, value]) => `${key}: ${value};`)
.join(" ")} }
`;
chart.appendChild(styleElement);
return chart;
}