Public
Edited
Jan 11, 2023
Insert cell
Insert cell
style = html`<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap');

h2 {
font-family:'Roboto', sans-serif;
font-weight:300;
font-size:1em;
fill:"#333333";
opacity:0.5;
max-width: 620px;
}

.styleTooltipChartLine{
font-family:'Roboto', sans-serif;
font-size:12px;
fill:#313131;
letter-spacing: 0.5px;
text-anchor: start;
}

.textTooltip{
fill:white;
}

</style>`
Insert cell
data = FileAttachment("datos.csv").csv({typed: true})
Insert cell
Insert cell
addTooltips(
Plot.plot({
width: 500,
height: 100,
inset: 20,
x: {
axis: null
},
y: {
axis: null
},
style: {
background: "#3a3a3a",
},
marks: [
Plot.lineY(data, {x: "date", y: "posicion", curve:"step-before", strokeWidth: 1, stroke: "#D9D9D9"}),
Plot.text(data, {x: "date", y: "posicion", text: d => `${d.posicion}º`, dy: -10, dx:-10,fontSize:10, fill:(d,i) => i == data.length - 1 ? "#FC9346" : "#e6e6e6"}),
Plot.dot(data, {
x: "date",
y: "posicion", r:2, fill:(d,i) => i == data.length - 1 ? "#FC9346" : "#e6e6e6"
}),
Plot.dot(data, {
x: "date",
y: "posicion", r:10, opacity:0,
title: (d) =>
`${numero_a_mes(d.date.getMonth())} ${d.date.getFullYear()} \n Posición: ${d.posicion}º` // \n makes a new line})
}),
],
}),"", "styleTooltipChartLine"
)
Insert cell
// Función para traducir un número a mes (0 > Enero y 11 > Diciembre)
numero_a_mes = (num) => {
const meses = ["ENERO", "FEBRERO", "MARZO", "ABRIL", "MAYO", "JUNIO", "JULIO", "AGOSTO", "SEPTIEMBRE", "OCTUBRE", "NOVIEMBRE","DICIEMBRE"];
for (let i = 0; i < meses.length; i++) {
if(num == i){
return meses[num];
}
}
}

Insert cell
numero_a_mes(0)
Insert cell
addTooltips = (chart, styles, claseTooltip) => {
const stroke_styles = { stroke: "blue", "stroke-width": 3 };
const fill_styles = { fill: "blue", opacity: 0.5 };

// Workaround if it's in a figure
const type = d3.select(chart).node().tagName;
let wrapper =
type === "FIGURE" ? d3.select(chart).select("svg") : d3.select(chart);

// Workaround if there's a legend....
const svgs = d3.select(chart).selectAll("svg");
if (svgs.size() > 1) wrapper = d3.select([...svgs].pop());
wrapper.style("overflow", "visible"); // to avoid clipping at the edges

// Set pointer events to visibleStroke if the fill is none (e.g., if its a line)
wrapper.selectAll("path").each(function (data, index, nodes) {
// For line charts, set the pointer events to be visible stroke
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 tip = wrapper
.selectAll(".hover")
.data([1])
.join("g")
.attr("class", claseTooltip)
.style("pointer-events", "none")
.style("text-anchor", "start")
.style("white-space", "initial");

// Add a unique id to the chart for styling
const id = id_generator();

// Add the event listeners
d3.select(chart).classed(id, true); // using a class selector so that it doesn't overwrite the ID
wrapper.selectAll("title").each(function () {
// Get the text out of the title, set it as an attribute on the parent, and remove it
const title = d3.select(this); // title element that we want to remove
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, wrapper.node());
if (text) tip.call(hover, pointer, text.split("\n"));
else tip.selectAll("*").remove();

// Raise it
d3.select(this).raise();
// Keep within the parent horizontally
const tipSize = tip.node().getBBox();
if (pointer[0] + tipSize.x < 0)
tip.attr(
"transform",
`translate(${tipSize.width / 2}, ${pointer[1]})`
);
else if (pointer[0] + tipSize.width / 2 > wrapper.attr("width"))
tip.attr(
"transform",
`translate(${wrapper.attr("width") - tipSize.width / 2}, ${
pointer[1] + 7
})`
);
})
.on("pointerout", function (event) {
tip.selectAll("*").remove();
// Lower it!
d3.select(this).lower();
});
});

// Remove the tip if you tap on the wrapper (for mobile)
wrapper.on("touchstart", () => tip.selectAll("*").remove());

// Define the styles
chart.appendChild(html`<style>
.${id} .has-title { cursor: pointer; pointer-events: all; }
.${id} .has-title:hover { ${Object.entries(styles).map(([key, value]) => `${key}: ${value};`).join(" ")} }`);

return chart;
}
Insert cell
// Function to position the tooltip
hover = (tip, pos, text) => {
const side_padding = 10;
const vertical_padding = 5;
const vertical_offset = 15;

// Empty it out
tip.selectAll("*").remove();

// Append the text
tip
.style("text-anchor", "start")
.style("pointer-events", "none")
.attr("transform", `translate(${pos[0]}, ${pos[1]})`)
.selectAll("text")
.data(text)
.join("text")
.attr('class', 'textTooltip') // estilo del texto del tooltip genérico en el CSS
.style("dominant-baseline", "ideographic")
.text((d) => d)
.attr("y", (d, i) => (i - (text.length - 1)) * 25 - vertical_offset)
.attr("x", (d, i) => (i === 0 ? 4 : 0)) // sumamos 3px a la primera línea para corregir un estilo inline en plot
.style("font-weight", (d, i) => (i === 0 ? "bold" : "normal"))
.style("letter-spacing", (d, i) => (i === 0 ? 3 : 1));

const bbox = tip.node().getBBox();

// Add a rectangle (as background)
tip
.append("rect")
.attr("y", bbox.y - vertical_padding)
.attr("x", bbox.x - side_padding)
.attr("width", bbox.width + side_padding * 2)
.attr("height", bbox.height + vertical_padding * 2)
.lower();
}
Insert cell
// To generate a unique ID for each chart so that they styles only apply to that chart
id_generator = () => {
var S4 = function () {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
};
return "a" + S4() + S4();
}
Insert cell
// Import Plot with additional tooltip functionality from this notebook
//import {Plot} from "@mkfreeman/plot-tooltip"
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more