CalendarMonth = (dataset, feature, setCellSize = 17, color = false) => {
let margin = setCellSize / 10;
const timeWeek = d3.utcMonday;
const countDay = d => (d.getUTCDay() + 6) % 7,
formatDate = d3.utcFormat("%x"),
format = d3.format("+.2%"),
formatDay = d => d3.timeFormat('%a')(d),
formatMonth = d3.utcFormat("%B");
let fontSize = d3.max([20, setCellSize / 2]);
const height = setCellSize * 6;
let width = height + setCellSize;
if (color == false) {
color = d3
.scaleSequential(d3.interpolateViridis)
.domain(d3.extent(dataset, d => +d[feature]));
}
function pathMonth(t) {
const n = 5;
const d = Math.max(0, Math.min(n, countDay(t)));
const w = timeWeek.count(d3.utcYear(t), t);
return `${
d === 0
? `M${w * setCellSize},0`
: d === n
? `M${(w + 1) * setCellSize},0`
: `M${(w + 1) * setCellSize},0V${d * setCellSize}H${w * setCellSize}`
}V${n * setCellSize}`;
}
const years = d3
.nest()
.key(d => d.date.getUTCFullYear())
.entries(dataset);
let SVG = (fontSize, height, width, setCellSize) =>
`<svg height="${height - setCellSize - fontSize}" width="${width -
setCellSize -
fontSize}" viewBox="${-setCellSize + fontSize} ${setCellSize * 0.6}
${width} ${height}" style="border:1px solid black"></svg>`;
const svgRef = html`${SVG(fontSize, height, width, setCellSize)}`;
let svg = d3.select(svgRef);
const year = svg
.selectAll("g")
.data(years)
.join("g")
.attr(
"transform",
(d, i) => `translate(40,${height * i + setCellSize * 1.5})`
);
year
.append("text")
.attr("font-weight", "bold")
.attr('font-size', fontSize * 2)
.attr('text-anchor', 'start')
.attr("x", -fontSize / 2)
.attr("dy", -2)
.text(d => d.key);
year
.append("g")
.attr("text-anchor", "end")
.selectAll("text")
.data(d3.range(2, 7).map(i => new Date(1995, 0, i)))
.join("text")
.attr("x", -2)
.attr("y", d => (countDay(d) + 0.5) * setCellSize)
.attr('font-family', 'monospace')
.attr('font-style', 'italic')
.attr("dy", -setCellSize / 2 + fontSize)
.attr('font-size', fontSize * 0.75)
.text(formatDay);
year
.append("g")
.selectAll("rect")
.data(d => d.values)
.join("rect")
.attr('stroke', 'black')
.attr("width", setCellSize - 1)
.attr("height", setCellSize - 1)
.attr("x", d => timeWeek.count(dataset[0].date, d.date) * setCellSize + 0.5)
//OLD: timeWeek.count(d3.utcYear(d.date), d.date) * setCellSize + 0.5
.attr("y", d => countDay(d.date) * setCellSize + 0.5)
.attr("fill", d => color(d[feature]))
.append("title")
.text(d => `${formatDate(d.date)}: ${format(d[feature])}`);
const month = year
.append("g")
.selectAll("g")
.data(d =>
d3.utcMonths(
d3.utcMonth(d.values[0].date),
d.values[d.values.length - 1].date
)
)
.join("g");
month
.filter((d, i) => i)
.append("path")
.attr("fill", "none")
.attr("stroke", "#fff")
.attr("stroke-width", 3)
.attr("d", pathMonth);
month
.append("text")
.attr(
"x",
d =>
timeWeek.count(dataset[0].date, dataset[dataset.length - 1].date) *
setCellSize +
setCellSize
)
//OLD: timeWeek.count(d3.utcYear(d), timeWeek.ceil(d)) * setCellSize + 2
.attr("y", -fontSize / 2)
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'end')
.attr('font-size', fontSize)
.style('font-style', 'italic')
.style('text-transform', 'uppercase')
.text(formatMonth);
return svgRef;
}