linePlot = (data, x, xlabel, y, z, color) => {
let ticks = d3.rollup(data, (v) => v[0].xlabel, (d) => d.x)
const state = [];
const chart = Plot.plot({
style: "overflow: visible;",
y: {
grid: true,
label: "↑ Value"
},
color: {
legend: true
},
marginBottom: 50,
x: {
label: "",
ticks: [...ticks.keys()],
tickRotate: -45,
tickFormat: (d) => ticks.get(d)
},
marks: [
Plot.line(data, {
x: x,
y: y,
z: z,
stroke: color,
title: (d) => `Subject: ${d[z]} Treatment: ${d[color]}`
}),
Plot.dot(data, {
x: x,
y: y,
z: z,
stroke: color,
r: 3,
fill: color,
title: (d) => `Subject: ${d[z]} \n X: ${d[x]} \n Y: ${d[y]}`
})
],
tooltip: {
stroke: color,
"stroke-width": "3px",
r: 3
}
});
const lines = d3.select(chart).select("[aria-label=line]").selectAll("path");
const Z = Plot.valueof(data, z);
lines
.on("pointerenter", function (event, i) {
lines.attr("stroke-opacity", (j) => (i === j ? 1 : 0.25));
})
.on("pointerout", () => lines.attr("stroke-opacity", 1))
.on("click", (event, I) => {
const owner = chart;
const v = Z[I[0]];
if (!Array.isArray(owner.value) || !event.metaKey) {
owner.value = [v];
} else {
if (owner.value.includes(v))
owner.value = owner.value.filter((d) => d !== v);
else owner.value.push(v);
}
owner.dispatchEvent(new CustomEvent("input"));
});
const points = d3
.select(chart)
.select("[aria-label=dot]")
.selectAll("circle");
points.on("click", (event, i) => {
const owner = chart;
const v = data[i];
owner.value = v;
owner.dispatchEvent(new CustomEvent("input"));
})
.on("pointerenter", function (event, i) {
points.attr("stroke-opacity", (j) => (i === j ? 1 : 0.25));
})
.on("pointerout", () => lines.attr("stroke-opacity", 1));
return chart;
}