Built with blockbuilder.org
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="https://npmcdn.com/babel-core@5.8.34/browser.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
| <script src='https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.js'></script> | |
| <style> | |
| body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; } | |
| * { | |
| font-family: Helvetica; | |
| } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| stroke: #000; | |
| shape-rendering: crispEdges; | |
| } | |
| #data { | |
| text-align: center; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <input id="loadfile" type="file" multiple /> | |
| <svg></svg> | |
| <div id='data'></div> | |
| <script type="text/babel"> | |
| /************** | |
| * This section modified from @enjalot's block: | |
| http://bl.ocks.org/enjalot/63d06e2ccadad0cb30dc5f920efd1cdf | |
| ***************/ | |
| d3.select("#loadfile").node() | |
| .addEventListener('change', loadFile, false); | |
| function loadFile(evt) { | |
| evt.stopPropagation(); | |
| evt.preventDefault(); | |
| var files; | |
| if(evt.dataTransfer) { | |
| files = evt.dataTransfer.files; | |
| } else { | |
| files = evt.target.files; | |
| } | |
| var i = 0; | |
| var reader = new FileReader(); | |
| reader.onload = function(e) { | |
| parseData(e.target.result); | |
| // start the next | |
| i += 1; | |
| files[i] && reader.readAsText(files[i]); | |
| } | |
| reader.readAsText(files[i]); | |
| } | |
| var allTransactions = []; | |
| function parseData(result) { | |
| var transactions = d3.csv.parse(result); | |
| // go through each transaction | |
| _.each(transactions, transaction => { | |
| if (transaction['Type'] === 'Sale') { | |
| allTransactions.push({ | |
| date: new Date(transaction['Trans Date']), | |
| amount: -1 * parseFloat(transaction['Amount']), | |
| title: transaction['Description'], | |
| }); | |
| } | |
| }); | |
| updateGraph(allTransactions); | |
| } | |
| // draw bar chart | |
| var width = 900; | |
| var height = 300; | |
| var barWidth = 1; | |
| var padding = {top: 40, left: 20}; | |
| var linearGradient = d3.select('svg').append('defs') | |
| .append("linearGradient") | |
| .attr("id", "linear-gradient") | |
| .attr({gradientUnits: 'userSpaceOnUse', | |
| x1: 0, | |
| y1: 0, | |
| x2: 0, | |
| y2: height - padding.top}); | |
| var svg = d3.select('svg') | |
| .attr({width, height}) | |
| .append('g'); | |
| var div = d3.select('div#data').style({width}); | |
| var timeFormat = d3.time.format('%a %b %e'); | |
| // do da linear gradient | |
| linearGradient.append("stop") | |
| .attr("offset", 0) | |
| .attr("stop-color", "#ed0345"); // start at red | |
| linearGradient.append("stop") | |
| .attr("offset", '25%') | |
| .attr("stop-color", "#fbbf45"); | |
| linearGradient.append("stop") | |
| .attr("offset", '75%') | |
| .attr("stop-color", "#aad962"); | |
| linearGradient.append("stop") | |
| .attr("offset", '100%') | |
| .attr("stop-color", "#03c383"); // end at green | |
| // initialize scales and axis | |
| var timeScale = d3.time.scale() | |
| .range([0, width - padding.left]); | |
| var xAxis = d3.svg.axis() | |
| .orient('bottom') | |
| .scale(timeScale); | |
| var heightScale = d3.scale.linear() | |
| .range([0, height - padding.top]); | |
| var yAxis = d3.svg.axis() | |
| .orient('left') | |
| .scale(heightScale); | |
| // initialize the containers | |
| var barsG = svg.append('g') | |
| .attr('transform', 'translate(' + padding.left + ',0)'); | |
| var xAxisG = svg.append('g') | |
| .classed('axis', true) | |
| .attr('transform', | |
| 'translate(' + padding.left + ',' + (height - padding.top) + ')'); | |
| var yAxisG = svg.append('g') | |
| .classed('axis', true) | |
| .attr('transform', 'translate(' + padding.left + ',0)'); | |
| function updateGraph(data) { | |
| // first group the data into days | |
| data = _.chain(data) | |
| .groupBy(d => d.date) | |
| .map((transactions) => { | |
| return { | |
| date: transactions[0].date, | |
| transactions, | |
| total: _.reduce(transactions, (sum, t) => { | |
| return t.amount ? sum + t.amount : sum; | |
| }, 0), | |
| }; | |
| }).sortBy(d => d.date).value(); | |
| barWidth = Math.floor((width - padding.left) / data.length) - 2; | |
| // update the scales | |
| timeScale.domain([data[0].date, _.last(data).date]); | |
| var maxTotal = d3.max(data, d => d.total); | |
| heightScale.domain([0, maxTotal]); | |
| xAxisG.call(xAxis); | |
| // yAxisG.call(yAxis); | |
| var bars = barsG.selectAll('rect').data(data, d => d.date); | |
| bars.enter().append('rect'); | |
| bars.exit().remove(); | |
| bars.on('mouseover', d => { | |
| var html = '<h1>' + timeFormat(d.date) + ': $' + d.total + '</h1>'; | |
| html += _.map(d.transactions, t => t.title + ' <b>$' + t.amount + '</b>').join('<br>'); | |
| div.html(html); | |
| }).style("fill", "url(#linear-gradient)") | |
| .attr({stroke: '#fff'}) | |
| .transition().duration(500) | |
| .attr({ | |
| x: d => timeScale(d.date) - barWidth / 2, | |
| y: d => height - padding.top - heightScale(d.total), | |
| width: barWidth, | |
| height: d => heightScale(d.total), | |
| }); | |
| } | |
| </script> | |
| </body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment