chart = ({
width = 1050,
height = 800,
data = aapl,
x = "Date",
y = "Close",
title = "Reusable little line chart",
source = "Source text goes here",
subtitle = "Can be embedded in other notebooks",
title_dy,
y_domain,
startvalue_dy = 0, endvalue_dy = 0, startvalue_dx = 0, endvalue_dx = 0,
good_or_bad,
multiplier,
x_labels,
numberFormat = d3.format("d"),
}={}) => {
data = data.toSorted((a,b) => d3.ascending(a[x],b[x]));
if(multiplier) data = data.map(m => {
const result = {...m};
if(multiplier === "thousands") result[y] = m[y]/1e3;
if(multiplier === "millions") result[y] = m[y]/1e6;
if(multiplier === "billions") result[y] = m[y]/1e9;
return result;
})
const extent = d3.extent(data, d=>d[x]);
const domainPadding = (extent[1] - extent[0]) / 20;
const domain = [
new Date(extent[0].valueOf() - domainPadding),
new Date(extent[1].valueOf() + domainPadding)
];
const ticks = x_labels ? (x_labels.split(", ")).map(m => d3.utcParse("%Y")(m)) : undefined;
const {fill, stroke} = getColor(good_or_bad);
subtitle = (subtitle || "") + (subtitle && multiplier ? ", " : "") + (multiplier || "");
const plot = Plot.plot({
width,
height,
marginTop: margin.top,
marginBottom: margin.bottom,
marginLeft: margin.left,
marginRight: margin.right,
x: {ticks, label: "", labelArrow: "none", tickFormat: dateFormat, tickSpacing: 100, fontVariant: "none"},
y: {ticks: [], domain: y_domain ? JSON.parse(y_domain) : undefined, label: "", labelArrow: "none"},
style: {fontFamily: fontFamily, fontWeight: weight, fontSize: fontSizeRegular, fill: black},
marks: [
Plot.areaY(data, {x, y, fill}),
//Plot.link(firstLast(data),{x1: 0, x2: 100, y1: 0, y2: 100}),
//Plot.lineY(firstLast(data), {x, y:0 ,stroke: "black", strokeWidth: 2}),
Plot.ruleY([0], {stroke: black, strokeWidth: 2}),
Plot.lineY(data, {
x, y,
markerStart: dot,
markerEnd: arrow,
stroke, strokeWidth, strokeLinejoin, strokeMiterlimit, strokeLinecap
}),
//Plot.dot(last(data), {x,y:d => 193,r: 10, fill: "black"}),
...markTitles(data, x, y, title_dy, title, subtitle, source),
...markFirstLastValues(data, x, y, numberFormat)
]
})
const xTickLabel = d3 //.select(plot)
.select('g[aria-label="x-axis tick label"]').select("text").node()
if (xTickLabel) {
const xTickLabelW = xTickLabel.getBBox().width;
d3.select(plot).select('g[aria-description="title"]')
.attr("transform", `translate(${0.5 - xTickLabelW/2},${0.5 -140 - (+title_dy || 0)})`)
d3.select(plot).select('g[aria-description="subtitle"]')
.attr("transform", `translate(${0.5 - xTickLabelW/2},${0.5 -100 - (+title_dy || 0)})`)
d3.select(plot).select('g[aria-description="source"]')
.attr("transform", `translate(${0.5 - xTickLabelW/2},${0.5 + 60})`)
d3.select(plot).select('g[aria-description="valueLeft"]')
.attr("transform", `translate(${0.5 - xTickLabelW/2 + +startvalue_dx},${0.5 - 35 - +startvalue_dy})`)
d3.select(plot).select('g[aria-description="dateLeft"]')
.attr("transform", `translate(${0.5 - xTickLabelW/2 + +startvalue_dx},${0.5 - 85 - +startvalue_dy})`)
d3.select(plot).select('g[aria-description="valueRight"]')
.attr("transform", `translate(${0.5 + xTickLabelW/2 + +endvalue_dx},${0.5 - 40 - +endvalue_dy})`)
d3.select(plot).select('g[aria-description="dateRight"]')
.attr("transform", `translate(${0.5 + xTickLabelW/2 + +endvalue_dx},${0.5 - 90 - +endvalue_dy})`)
d3.select(plot).select('g[aria-description="compareRight"]')
.attr("transform", `translate(${0.5 + xTickLabelW/2 + +endvalue_dx},${0.5 - 40 - +endvalue_dy})`)
}
//console.log(texts);
plot.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "style"))
.innerHTML = fontFace;
return plot;
}