73 lines
2.2 KiB
TypeScript
73 lines
2.2 KiB
TypeScript
import * as d3 from "d3";
|
|
import { formatCurrencyCrude, tooltip, formatCurrency } from "./utils";
|
|
import _ from "lodash";
|
|
import COLORS from "./colors";
|
|
|
|
export function renderYearlySpends(
|
|
svgNode: SVGElement,
|
|
yearlySpends: { [year: string]: { [month: string]: number } }
|
|
) {
|
|
const BAR_HEIGHT = 20;
|
|
const svg = d3.select(svgNode),
|
|
margin = { top: 15, right: 20, bottom: 20, left: 70 },
|
|
width = svgNode.parentElement.clientWidth - margin.left - margin.right,
|
|
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
|
|
|
const color = COLORS.expenses;
|
|
|
|
const height = BAR_HEIGHT * Object.keys(yearlySpends).length;
|
|
svg.attr("height", height + margin.top + margin.bottom);
|
|
|
|
interface Point {
|
|
year: string;
|
|
value: number;
|
|
breakdown: { [month: string]: number };
|
|
}
|
|
|
|
const points: Point[] = _.chain(yearlySpends)
|
|
.map((breakdown, year) => {
|
|
const value = _.sum(_.values(breakdown));
|
|
return { year, breakdown, value };
|
|
})
|
|
.sortBy((p) => p.year)
|
|
.value();
|
|
|
|
const x = d3.scaleLinear().range([0, width]);
|
|
const y = d3.scaleBand().range([height, 0]).paddingInner(0.2).paddingOuter(0);
|
|
|
|
y.domain(points.map((p) => p.year));
|
|
x.domain([0, d3.max(points, (p: Point) => p.value)]);
|
|
|
|
g.append("g")
|
|
.attr("class", "axis y")
|
|
.attr("transform", "translate(0," + height + ")")
|
|
.call(d3.axisBottom(x).tickSize(-height).tickFormat(formatCurrencyCrude));
|
|
|
|
g.append("g").attr("class", "axis y dark").call(d3.axisLeft(y));
|
|
|
|
g.append("g")
|
|
.selectAll("rect")
|
|
.data(points)
|
|
.join("rect")
|
|
.attr("stroke-opacity", 0.6)
|
|
.attr("fill-opacity", 0.4)
|
|
.attr("stroke", color)
|
|
.attr("fill", color)
|
|
.attr("data-tippy-content", (d) => {
|
|
return tooltip(
|
|
_.map(d.breakdown, (value, month) => {
|
|
return [month, [formatCurrency(value), "has-text-right has-text-weight-bold"]];
|
|
}),
|
|
{ total: formatCurrency(d.value), header: d.year }
|
|
);
|
|
})
|
|
.attr("x", x(0))
|
|
.attr("y", function (d) {
|
|
return y(d.year) + (y.bandwidth() - Math.min(y.bandwidth(), BAR_HEIGHT)) / 2;
|
|
})
|
|
.attr("width", function (d) {
|
|
return x(d.value) - x(0);
|
|
})
|
|
.attr("height", y.bandwidth());
|
|
}
|