Skip to content

Instantly share code, notes, and snippets.

@mhkeller
Created January 24, 2013 06:58
Show Gist options
  • Save mhkeller/4618211 to your computer and use it in GitHub Desktop.
Save mhkeller/4618211 to your computer and use it in GitHub Desktop.
Sample d3 gantt chart, sorting works, filtering broken
<title>Ganttttttttttttt</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.2/underscore-min.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: none;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.company-bar {
fill:#fc0;
}
.bar-label {
color:black;
font-family: sans-serif;
font-size: 11px;
text-anchor:end;
}
/* HTML Styles*/
body{
font-family: "HelveticaNeue-Light", "Helvetica Neue Light","Helvetica Neue", Helvetica,sans-serif;
font-weight: 300;
}
h1,h2,h3,h4,h5,h6,ul{
margin:0;
}
ul li{
font-size:14px;
}
.sort-order{
font-size:11px;
letter-spacing:1px;
font-weight:bold;
}
</style>
</head>
<body>
<h4>Sorters</h4>
<ul id="btns">
<li data-sorter="name">
<a href="#">Sort by name</a>
</li>
<li data-sorter="startDate">
<a href="#">Sort by start date</a>
</li>
</ul>
<script>
var dataSet = [
{
"startDate":"1/1/12",
"endDate":"2/1/12",
"sex":"male",
"companyName":"Vandelay Industries",
},
{
"startDate":"5/2/12",
"endDate":"4/31/13",
"sex":"male",
"companyName":"Dr. Martin Van Nostrand"
},
{
"startDate":"6/14/12",
"endDate":"8/11/12",
"sex":"male",
"companyName":"Uncle Leo"
},
{
"startDate":"3/1/12",
"endDate":"7/31/12",
"sex":"female",
"companyName":"Jackie Chiles"
},
{
"startDate":"3/1/12",
"endDate":"7/31/12",
"sex":"female",
"companyName":"Crazy Joe Davola"
},
{
"startDate":"11/1/12",
"endDate":"12/31/13",
"sex":"male",
"companyName":"Lloyd Braun"
},
{
"startDate":"12/1/11",
"endDate":"6/15/12",
"sex":"female",
"companyName":"Sue Ellen Mishkey"
}
]
var dataInfo = {};
// D3 requires each object to have a unique key for object constancy
// http://bost.ocks.org/mike/constancy/
// We can use underscore to create that if we're not sure if they exist in our data
// Useful if you're updating data quickly (realtime, daily) and need otherwise meaningless uids
// Also useful for making an associative array for hover info
for (var datum in dataSet){
if(_.has(dataSet, datum)){
var uid = _.uniqueId('a');
// Add unique id
dataSet[datum]['uid'] = uid
// Create associate array
dataInfo[uid] = dataSet[datum];
}
}
var DATA ={
dt: dataSet
}
var window_width = $(document).width()
// How many projects do you have?
var prjs = dataSet.length;
// Toggler
var toggler = true;
// Dimensions
var bar_height = 20;
var row_height = bar_height + 10;
var vertical_padding = 150
var bar_start_offset = 40
var w = window_width - 100;
var h = prjs * row_height + vertical_padding;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
function getDate(date) {
return new Date(date);
}
function accessStartDate(d) { return getDate(d.startDate); }
function accessEndDate(d) { return getDate(d.endDate); }
var min = d3.min(dataSet, accessStartDate);
var max = d3.max(dataSet, accessEndDate);
var paddingLeft = 150;
var paddingTop = 50;
var xScale = d3.time.scale()
.domain([min,max]).nice()
.range([paddingLeft, w]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
// Lines
var line = svg.append("g")
.selectAll("line")
.data(xScale.ticks(10))
.enter().append("line")
.attr("x1", xScale)
.attr("x2", xScale)
.attr("y1", paddingTop + 30)
.attr("y2", h-50)
.style("stroke", "#ccc");
var y = function(i){
return i*row_height+paddingTop + bar_start_offset;
}
labelY = function(i){
return i * row_height + paddingTop + bar_start_offset + 13;
}
// Company bars
var bar = svg.selectAll("rect")
.data(DATA.dt, function(key){return key.uid});
bar.enter().append("rect")
.attr("y", function(d, i) { ;return y(i) })
.attr("x", function(d) { return xScale(getDate(d.startDate))} )
.attr("width", function(d) { return (xScale(getDate(d.endDate)) - xScale(getDate(d.startDate))) } )
.attr("height", bar_height)
.attr("class", "company-bar")
.on("mouseover", function(d){d3.select(this).style("fill", "#F5AF00"); getCompanyData(String(d.uid)) })
.on("mouseout", function() {d3.select(this).style("fill", "#fc0");});
// Company Labels
var label = svg.selectAll("text")
.data(DATA.dt, function(key){return key.uid});
label.enter().append("text")
.attr("class", "bar-label")
// .attr("text-anchor","end")
.attr("x", paddingLeft-10)
.attr("y", function(d, i) { return labelY(i); })
.text(function(d){return d.companyName});
// Bottom Axis
var btmAxis = svg.append("g")
.attr("transform", "translate(0,"+(h - 25)+")")
.attr("class", "axis")
.call(xAxis);
// Top Axis
var topAxis = svg.append("g")
.attr("transform", "translate(0,"+paddingTop+")")
.attr("class", "axis")
.call(xAxis);
function animateRows(){
bar.data(DATA.dt, function(d){return d.uid})
.transition()
.duration(400)
.attr("y", function(d, i) {return y(i) });
label.data(DATA.dt, function(d){return d.uid})
.transition()
.duration(400)
.attr("y", function(d, i) {return labelY(i) });
}
function toggleSort(){
toggler = !toggler;
}
// Name sorters
function sortByName(){
if(!toggler){
sortByNameAsc();
}else{
sortByNameDsc();
}
}
function sortByNameAsc(){
DATA.dt.sort(function(a, b){
return d3.ascending(a.companyName, b.companyName);
})
}
function sortByNameDsc(){
DATA.dt.sort(function(a, b){
return d3.descending(a.companyName, b.companyName);
})
}
// Start Date sorters
function sortByStartDate(){
console.log('b',DATA.dt)
if(!toggler){
sortByStartDateAsc();
}else{
sortByStartDateDsc();
}
console.log('a',DATA.dt)
}
function sortByStartDateAsc(){
DATA.dt.sort(function(a, b){
return d3.ascending(getDate(a.startDate), getDate(b.startDate));
})
}
function sortByStartDateDsc(){
DATA.dt.sort(function(a, b){
return d3.descending(getDate(a.startDate), getDate(b.startDate));
})
}
// Extra text so you know if you're ascending or descending
function sortOrderText(){
if (toggler){
return '<span class="sort-order">(asc)</span>';
}else{
return '<span class="sort-order">(dsc)</span>';
}
}
// Hover info
function getCompanyData(uid){
console.log(dataInfo[uid])
}
// Sorters
$('#btns li').click( function(){
$('.sort-order').remove();
var sorter = $(this).attr('data-sorter');
toggleSort();
if (sorter == 'name'){
sortByName();
}else if (sorter == 'startDate'){
sortByStartDate()
}
animateRows();
$(this).append(sortOrderText())
});
function filterJSON(json, key, value) {
var result = [];
for (var index in json) {
if (json[index][key] === value) {
result.push(json[index]);
// result[index] = json[index];
}
}
return result;
}
$(document).keyup(function(e) {
console.log(e.keyCode)
if (e.keyCode == 27) { /// esc
var filteredData = filterJSON(dataSet, "sex", 'female');
// console.log(filteredData)
DATA.dt = filteredData;
svg.selectAll('rect')
.data(DATA.dt, function(d){return d.uid})
.enter().append("rect")
.attr("y", function(d, i) { console.log('female',d);return y(i) })
.attr("x", function(d) { return xScale(getDate(d.startDate))} )
.attr("width", function(d) { return (xScale(getDate(d.endDate)) - xScale(getDate(d.startDate))) } )
.attr("height", bar_height)
.attr("class", "company-bar")
.on("mouseover", function(d){d3.select(this).style("fill", "#F5AF00"); getCompanyData(String(d.uid)) })
.on("mouseout", function() {d3.select(this).style("fill", "#fc0");});
svg.selectAll('text')
.data(DATA.dt, function(d){return d.uid})
.enter().append("text")
.attr("class", "bar-label")
.attr("x", paddingLeft-10)
.attr("y", function(d, i) { return labelY(i); })
.text(function(d){return d.companyName});
svg.selectAll('rect').data(DATA.dt, function(d){return d.uid})
.exit().remove();
svg.selectAll('.bar-label').data(DATA.dt, function(d){return d.uid})
.exit().remove();
}else if(e.keyCode == 16){ // shift
var filteredData = filterJSON(dataSet, "sex", 'male');
// console.log(filteredData)
DATA.dt = filteredData
svg.selectAll('rect')
.data(DATA.dt, function(d){return d.uid})
.enter().append("rect")
.attr("y", function(d, i) { console.log('male',d);return y(i) })
.attr("x", function(d) { return xScale(getDate(d.startDate))} )
.attr("width", function(d) { return (xScale(getDate(d.endDate)) - xScale(getDate(d.startDate))) } )
.attr("height", bar_height)
.attr("class", "company-bar")
.on("mouseover", function(d){d3.select(this).style("fill", "#F5AF00"); getCompanyData(String(d.uid)) })
.on("mouseout", function() {d3.select(this).style("fill", "#fc0");});
svg.selectAll('text')
.data(DATA.dt, function(d){return d.uid})
.enter().append("text")
.attr("class", "bar-label")
.attr("x", paddingLeft-10)
.attr("y", function(d, i) { return labelY(i); })
.text(function(d){return d.companyName});
svg.selectAll('rect').data(DATA.dt, function(d){return d.uid})
.exit().remove();
svg.selectAll('.bar-label').data(DATA.dt, function(d){return d.uid})
.exit().remove();
}else if(e.keyCode == 192){ // tilde
DATA.dt = dataSet;
svg.selectAll('rect')
.data(DATA.dt, function(d){return d.uid})
.enter().append("rect")
.attr("y", function(d, i) { console.log('male',d);return y(i) })
.attr("x", function(d) { return xScale(getDate(d.startDate))} )
.attr("width", function(d) { return (xScale(getDate(d.endDate)) - xScale(getDate(d.startDate))) } )
.attr("height", bar_height)
.attr("class", "company-bar")
.on("mouseover", function(d){d3.select(this).style("fill", "#F5AF00"); getCompanyData(String(d.uid)) })
.on("mouseout", function() {d3.select(this).style("fill", "#fc0");});
svg.selectAll('text')
.data(DATA.dt, function(d){return d.uid})
.enter().append("text")
.attr("class", "bar-label")
.attr("x", paddingLeft-10)
.attr("y", function(d, i) { return labelY(i); })
.text(function(d){return d.companyName});
svg.selectAll('rect').data(DATA.dt, function(d){return d.uid})
.exit().remove();
svg.selectAll('.bar-label').data(DATA.dt, function(d){return d.uid})
.exit().remove();
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment