Skip to content

Instantly share code, notes, and snippets.

@biovisualize
Last active August 27, 2015 13:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save biovisualize/31eb70a0880f66262cda to your computer and use it in GitHub Desktop.
Save biovisualize/31eb70a0880f66262cda to your computer and use it in GitHub Desktop.
Simple GitHub Calendar

A simple version of the GiHub commit calendar, just to try to simplify this exemple by using d3.time utilities. if you need this type of calendar, you should know about the cal-heatmap plugin.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
.cell {
stroke: silver;
}
.label {
font-size: 10px;
font-family: sans-serif;
fill: #666;
}
.tooltip {
border: silver 1px solid;
background-color: white;
position: absolute;
pointer-events: none;
border-radius: 8px 8px 0px 8px;
padding: 5px;
opacity: 0.8;
font-size: 10px;
font-family: sans-serif;
}
.hovered {
stroke: black;
}
.cell-hover {
pointer-events: none;
stroke-width: 2px;
stroke: black;
}
.cell.out-of-range {
display: none;
}
</style>
</head>
<body>
<div class="container"></div>
<div class="tooltip"></div>
<script>
var firstDayOfTheMonth = new Date('01/01/2015');
var lastDayOfTheMonth = new Date('12/31/2015');
var firstDayOfWeekBeforeFirstDayOfMonth = d3.time.week.offset(firstDayOfTheMonth, -1);
var data = d3.time.weeks(firstDayOfWeekBeforeFirstDayOfMonth, lastDayOfTheMonth)
.map(function(d){
var date = new Date(d);
return d3.time.days(date, d3.time.week.offset(date, 1)).map(function(dB){ return {date: dB, value: ~~(Math.random()*100)}; });
});
var firstIncompleteWeek = d3.time.days(firstDayOfWeekBeforeFirstDayOfMonth, new Date(data[0][0].date));
var lastIncompleteWeek = d3.time.days(lastDayOfTheMonth, new Date(data[data.length-1][data[0].length -1].date));
var dataValues = d3.merge(data).map(function(d){ return d.value; });
// nullify days out of range
data[0].forEach(function(d, i){
if(i < data[0].length - firstIncompleteWeek.length){
d.value = null;
}
});
data[data.length-1].forEach(function(d, i){
if(i > data[0].length - 1 - lastIncompleteWeek.length){
d.value = null;
}
});
var config = {
width: 1000,
height: 250,
margin: {top: 80, right: 10, bottom: 40, left: 30},
dateFormatX: d3.time.format('%d %b'),
dateFormatY: d3.time.format('%a'),
dateFormatTooltip: d3.time.format('%d %b, %Y'),
labelAxisPadding: 5,
colorScale: d3.scale.linear().domain(d3.extent(dataValues)).range(['white', 'skyblue'])
};
config.chartWidth = config.width - config.margin.left - config.margin.right;
config.chartHeight = config.height - config.margin.top - config.margin.bottom;
config.cellWidth = Math.floor(config.chartWidth / data.length);
config.cellHeight = ~~(config.chartHeight / data[0].length);
var tooltip = d3.select('.tooltip').style({display: 'none'});
var chartContainer = d3.select('.container')
.append('svg')
.attr({
width: config.width,
height: config.height
})
.append('g')
.attr({
transform: 'translate(' + [config.margin.left, config.margin.top] + ')'
});
var columns = chartContainer.selectAll('g.column')
.data(data);
columns.enter().append('g')
.attr({
'class': 'column'
});
columns.selectAll('rect.cell')
.data(function(d){ return d; })
.enter().append('rect')
.classed('cell', true)
.classed('out-of-range', function(d){ return d.value === null; })
.attr({
x: function(d, i, pI){ return config.cellWidth * pI; },
y: function(d, i, pI){ return config.cellHeight * i; },
width: config.cellWidth,
height: config.cellHeight,
fill: function(d){ return config.colorScale(d.value); }
})
.on('mousemove', function(d){
var currentCell = d3.select(this);
var currentCellX = currentCell.attr('x');
var currentCellY = currentCell.attr('y');
tooltip.html(config.dateFormatTooltip(d.date) + '<br>Value: ' + d.value)
.style({
display: 'block',
left: function(){
return d3.event.clientX - this.offsetWidth + 'px';
},
top: function(){
return d3.event.clientY - this.offsetHeight + 'px';
}
});
cellHover.attr({
x: currentCellX,
y: currentCellY,
fill: config.colorScale(d.value)
})
.style({display: 'block'})
})
.on('mouseout', function(){
tooltip.style({ display: 'none'});
cellHover.style({display: 'none'})
});
columns.exit().remove();
chartContainer.selectAll('text.label.x')
.data(d3.transpose(data)[0])
.enter().append('text')
.attr({
'class': 'label x',
x: function(d, i){ return config.cellWidth * i; },
dy: function(d, i){ return - config.labelAxisPadding; },
transform: function(d, i){
return 'rotate(-60, ' + (config.cellWidth * i + 5) + ', -10)';
}
})
.text(function(d){ return config.dateFormatX(new Date(d.date)); });
chartContainer.selectAll('text.label.y')
.data(data[0])
.enter().append('text')
.attr({
'class': 'label y',
y: function(d, i){ return config.cellHeight * i + config.cellHeight / 2; }
})
.text(function(d){ return config.dateFormatY(new Date(d.date)); })
.attr({
dx: function(d, i){ return -this.getBBox().width - config.labelAxisPadding; }
});
var cellHover = chartContainer.append('rect')
.style({ display: 'none'})
.attr({
'class': 'cell-hover',
width: config.cellWidth,
height: config.cellHeight
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment