|
function TimelineChart() { |
|
|
|
// assumptions about the data passed to this chart: |
|
// i.e. dataset of each selection |
|
// -> list of objects. each object has the following keys: |
|
// -> user_id, occurred_at, event_type |
|
|
|
// chart config |
|
var circleRadius = 20 |
|
, margin = {top: 2, right: circleRadius+2, bottom: 2, left: circleRadius+2} |
|
, width = 960 - margin.left - margin.right // TODO: depends on container |
|
, height = circleRadius*2 |
|
, toDate = function(d) { return d.occurred_at } |
|
, x = d3.time.scale() |
|
, xScaleRound = d3.time.day |
|
|
|
function drawChart(selection) { |
|
selection.each(function(dataset, i) { |
|
|
|
// update scales |
|
x |
|
.domain(d3.extent(dataset, function(d) { return new Date(toDate(d)) })) |
|
.range([0, width]) |
|
.nice(xScaleRound) |
|
|
|
// select chart element(s) (eg. innerchart) if they exist already |
|
var innerchart = d3.select(this).selectAll('g.innerchart') |
|
.data(function (d) { return [d] }) |
|
|
|
// otherwise create them and the elements they contain |
|
var innerchartEnter = innerchart.enter() |
|
.append('g') |
|
.classed('innerchart', true) |
|
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') |
|
|
|
innerchartEnter.append('line') |
|
.attr('x1', x.range()[0]) |
|
.attr('y1', height/2) |
|
.attr('x2', x.range()[1]) |
|
.attr('y2', height/2) |
|
|
|
// update data |
|
var circles = innerchart.selectAll('circle') |
|
.data(function(d) { return d }, function(d) { return d.occurred_at }) |
|
|
|
circles.enter() |
|
.append('circle') |
|
.style('fill', 'gray') |
|
.style('opacity', 0.2) |
|
.attr('cx', function(d) { return x(new Date(toDate(d))) }) |
|
.attr('cy', height/2) |
|
.attr('r', 0) |
|
.transition() |
|
.duration(200) |
|
.attr('r', circleRadius) |
|
|
|
circles.exit() |
|
.transition() |
|
.duration(200) |
|
.attr('r', 0) |
|
.style('opacity', 0) |
|
.remove() |
|
|
|
}) |
|
} |
|
|
|
return drawChart |
|
|
|
} |