addTooltips = (chart, hover_styles = { fill: "blue", opacity: 0.5 }) => {
let styles = hover_styles;
const line_styles = {
stroke: "blue",
"stroke-width": 3
};
const type = d3.select(chart).node().tagName;
const wrapper =
type === "FIGURE" ? d3.select(chart).select("svg") : d3.select(chart);
wrapper.style("overflow", "visible");
wrapper.selectAll("path").each(function (data, index, nodes) {
if (
d3.select(this).attr("fill") === null ||
d3.select(this).attr("fill") === "none"
) {
d3.select(this).style("pointer-events", "visibleStroke");
styles = _.isEqual(hover_styles, { fill: "blue", opacity: 0.5 })
? line_styles
: hover_styles;
}
});
const tip = wrapper
.selectAll(".hover-tip")
.data([""])
.join("g")
.attr("class", "hover")
.style("pointer-events", "none")
.style("text-anchor", "middle");
const id = id_generator();
d3.select(chart)
.classed(id, true)
.selectAll("title")
.each(function () {
const title = d3.select(this);
const parent = d3.select(this.parentNode);
const t = title.text();
if (t) {
parent.attr("__title", t).classed("has-title", true);
title.remove();
}
parent
.on("mousemove", function (event) {
const text = d3.select(this).attr("__title");
const pointer = d3.pointer(event, wrapper.node());
if (text) tip.call(hover, pointer, text.split("\n"));
else tip.selectAll("*").remove();
d3.select(this).raise();
const tipSize = tip.node().getBBox();
if (pointer[0] + tipSize.x < 0)
tip.attr(
"transform",
`translate(${tipSize.width / 2}, ${pointer[1] + 7})`
);
else if (pointer[0] + tipSize.width / 2 > wrapper.attr("width"))
tip.attr(
"transform",
`translate(${wrapper.attr("width") - tipSize.width / 2}, ${
pointer[1] + 7
})`
);
})
.on("mouseout", function (event) {
tip.selectAll("*").remove();
d3.select(this).lower();
});
});
wrapper.on("touchstart", () => tip.selectAll("*").remove());
const style_string = Object.keys(styles)
.map((d) => {
return `${d}:${styles[d]};`;
})
.join("");
const style = html`<style>
.${id} .has-title {
cursor: pointer;
pointer-events: all;
}
.${id} .has-title:hover {
${style_string}
}
</style>`;
chart.appendChild(style);
return chart;
}