calendar = (
selection,
{
year = new Date().getFullYear(),
month = new Date().getMonth() + 1,
width = 400,
height = 400,
monthLabels,
dowLabels,
data = [],
color
} = {}
) => {
if (!monthLabels) {
monthLabels = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec'
];
}
if (!dowLabels) {
dowLabels = 'MTWTFSS'.split('');
}
const dates = datesOfMonth({ year, month });
const margin = {
top: height * 0.1,
right: width * 0.1,
bottom: height * 0.1,
left: width * 0.1
};
const x = d3
.scaleBand()
.domain(d3.range(7))
.range([margin.left, width - margin.right]);
const y = d3
.scaleBand()
.domain(d3.range(7))
.range([margin.top, height - margin.bottom]);
const cell = { width: x.bandwidth(), height: y.bandwidth() };
const firstDateDay = dates[0].getDay() - 1 < 0 ? 6 : dates[0].getDay() - 1;
const g = selection; //.append('g')
g.attr('font-size', 14).attr('font-family', 'sans-serif');
// g.append('rect')
// .attr('x', margin.left)
// .attr('y', margin.top)
// .attr('width', width - margin.left - margin.right)
// .attr('height', height - margin.top - margin.bottom)
// .attr('fill', 'none')
// .attr('stroke', 'gray');
// g.append('text')
// .classed('month', true)
// .attr('x', 10)
// .attr('y', 20)
// .text(`${monthLabels[month - 1]}`);
// g.selectAll('.dow')
// .data(dowLabels)
// .join('text')
// .classed('dow', true)
// .attr('text-anchor', 'middle')
// .text(d => d)
// .attr('x', (d, i) => x(i))
// .attr('y', (d, i) => 40);
const getData = d =>
data.find(({ date }) => date.getTime() === d.getTime())
? data.find(({ date }) => date.getTime() === d.getTime()).value
: 0;
if (!color) {
color = d => ['pink', 'yellow'][d % 2];
}
g.selectAll('rect.date-bg')
.data(dates)
.join('rect')
.attr('x', (d, i) => x((i + firstDateDay) % 7))
.attr('y', (d, i) => y(Math.floor((i + firstDateDay) / 7)))
.attr('width', cell.width)
.attr('height', cell.height)
.classed('date-bg', true)
.attr('fill', d => color(getData(d)) || 'none');
g.selectAll('text.date')
.data(dates)
.join('text')
.classed('date', true)
.attr('font-size', cell.height * 0.25)
.attr('font-weight', 600)
.attr('text-anchor', 'middle')
.attr('opacity', 0.5)
.attr('ominant-baseline', 'center')
.text(d => d3.timeFormat('%-d')(d))
.attr('x', (d, i) => x((i + firstDateDay) % 7) + cell.width / 2)
.attr(
'y',
(d, i) => y(Math.floor((i + firstDateDay) / 7)) + cell.height * 0.25
);
g.selectAll('text.value')
.data(data)
.join('text')
.classed('value', true)
.attr('text-anchor', 'middle')
// .attr('ominant-baseline', 'center')
// .text(d => d3.format('.0f')(d.value))
.text(d => (d.value ? d3.format('.0%')(d.value) : ''))
.attr('x', (d, i) => x((i + firstDateDay) % 7) + cell.width / 2)
.attr(
'y',
(d, i) => y(Math.floor((i + firstDateDay) / 7)) + cell.height * 0.75
);
}