|
// table.js |
|
|
|
// Same format we'd get from d3.csv |
|
var data = [ |
|
{ title: "The Godfather", year: 1972, length: 175, budget: 6000000, rating: 9.1 }, |
|
{ title: "The Shawshank Redemption", year: 1994, length: 142, budget: 25000000, rating: 9.1 }, |
|
{ title: "The Lord of the Rings: The Return of the King", year: 2003, length: 251, budget: 94000000, rating: 9 }, |
|
{ title: "The Godfather: Part II", year: 1974, length: 200, budget: 13000000, rating: 8.9 }, |
|
{ title: "Shichinin no samurai", year: 1954, length: 206, budget: 500000, rating: 8.9 }, |
|
{ title: "Buono, il brutto, il cattivo, Il", year: 1966, length: 180, budget: 1200000, rating: 8.8 }, |
|
{ title: "Casablanca", year: 1942, length: 102, budget: 950000, rating: 8.8 }, |
|
{ title: "The Lord of the Rings: The Fellowship of the Ring", year: 2001, length: 208, budget: 93000000, rating: 8.8 }, |
|
{ title: "The Lord of the Rings: The Two Towers", year: 2002, length: 223, budget: 94000000, rating: 8.8 }, |
|
{ title: "Pulp Fiction", year: 1994, length: 168, budget: 8000000, rating: 8.8 } |
|
]; |
|
|
|
// Define column structure / formatting |
|
var columns = [ |
|
{ head: 'Movie title', cl: 'title', html: d3.f('title') }, |
|
{ head: 'Year', cl: 'center', html: d3.f('year') }, |
|
{ head: 'Length', cl: 'center', html: d3.f('length', length()) }, |
|
{ head: 'Budget', cl: 'num', html: d3.f('budget', d3.format('$,')) }, |
|
{ head: 'Rating', cl: 'num', html: d3.f('rating', d3.format('.1f')) } |
|
]; |
|
|
|
// Previously done on every row data join. Now one and done. |
|
var movies = data.map(row => |
|
columns.map(c => |
|
Object.assign({}, c, {html: c.html(row)}))); |
|
|
|
// create table |
|
var table = d3.select('table'); |
|
var thead = table.append('thead').append('tr'); |
|
var tbody = table.append('tbody'); |
|
|
|
/** |
|
* Toggle menu |
|
*/ |
|
var toggles = d3.select('#toggles'); |
|
|
|
var labels = toggles.append('ul').selectAll('li') |
|
.data(columns) |
|
.enter().append('li') |
|
.style('list-style', 'none') |
|
.append('label'); |
|
|
|
labels.insert('input') |
|
.attr('id', d => d.head) |
|
.attr('class', 'toggle') |
|
.attr('type', 'checkbox') |
|
.property('checked', true); |
|
|
|
labels.append('text') |
|
.text(d => d.head); |
|
|
|
toggles.append('button') |
|
.attr('type', 'button') |
|
.attr('onclick', 'checkAll()') |
|
.text('Check all') |
|
.on('click touch', change); |
|
|
|
toggles.append('button') |
|
.attr('type', 'button') |
|
.attr('onclick', 'uncheckAll()') |
|
.text('Clear selection'); |
|
|
|
toggles.on('change', change); |
|
|
|
// helper functions |
|
function length() { |
|
var fmt = d3.format('02d'); |
|
return l => Math.floor(l / 60) + ':' + fmt(l % 60) + ''; |
|
} |
|
|
|
function checkAll() { |
|
d3.selectAll('.toggle').property('checked', true); |
|
} |
|
|
|
function uncheckAll() { |
|
d3.selectAll('.toggle').property('checked', false); |
|
} |
|
|
|
function change() { |
|
d3.transition().each(redraw); |
|
} |
|
|
|
change(); |
|
|
|
/** |
|
* Update table on menu change |
|
*/ |
|
|
|
function redraw() { |
|
var checked = d3.selectAll('.toggle:checked')[0] |
|
.map(x => x.id); |
|
|
|
var checked_columns = columns.filter(x => checked.indexOf(x.head) != -1); |
|
|
|
/** |
|
* Table header |
|
*/ |
|
var head = thead.selectAll('th') |
|
.data(checked_columns, |
|
d => d.head); |
|
|
|
// Nothing to update |
|
head.enter() |
|
.append('th') |
|
.attr('class', d3.f('cl')) |
|
.text(d3.f('head')); |
|
|
|
head.exit().remove(); |
|
|
|
/** |
|
* Table body |
|
*/ |
|
var checked_movies = movies.map(row => |
|
row.filter(c => checked.indexOf(c.head) != -1)); |
|
|
|
// Rows |
|
var tr = tbody.selectAll('tr') |
|
.data(checked_movies); |
|
|
|
tr.enter().append('tr'); |
|
tr.exit().remove(); |
|
|
|
// Cells |
|
var td = tr.selectAll('td') |
|
.data(row => row, d => d.head); |
|
|
|
td.exit().remove(); |
|
|
|
td.enter().append('td') |
|
.attr('class', d3.f('cl')) |
|
.html(d3.f('html')); |
|
|
|
head.order(); |
|
td.order(); |
|
} |