/* ------------------------------------------------------------------------------ * * # D3.js - horizontal bar chart * * Demo d3.js horizontal bar chart setup with .csv data source * * ---------------------------------------------------------------------------- */ // Setup module // ------------------------------ var DashboardAreas = function() { // // Setup module components // // Monthly sales area chart var _MonthlySalesAreaChart = function(element, height, color) { if (typeof d3 == 'undefined') { console.warn('Warning - d3.min.js is not loaded.'); return; } // Initialize chart only if element exsists in the DOM if($(element).length > 0) { // Basic setup // ------------------------------ // Define main variables var d3Container = d3.select(element), margin = {top: 20, right: 35, bottom: 40, left: 35}, width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right, height = height - margin.top - margin.bottom; // Date and time format var parseDate = d3.time.format('%Y-%m-%d').parse, bisectDate = d3.bisector(function(d) { return d.date; }).left, formatDate = d3.time.format('%b %d'); // Create SVG // ------------------------------ // Container var container = d3Container.append('svg'); // SVG element var svg = container .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') // Construct chart layout // ------------------------------ // Area var area = d3.svg.area() .x(function(d) { return x(d.date); }) .y0(height) .y1(function(d) { return y(d.value); }) .interpolate('monotone') // Construct scales // ------------------------------ // Horizontal var x = d3.time.scale().range([0, width ]); // Vertical var y = d3.scale.linear().range([height, 0]); // Create axes // ------------------------------ // Horizontal var xAxis = d3.svg.axis() .scale(x) .orient('bottom') .ticks(d3.time.days, 6) .innerTickSize(4) .tickPadding(8) .tickFormat(d3.time.format('%b %d')); // Load data // ------------------------------ d3.json('../../../../global_assets/demo_data/dashboard/monthly_sales.json', function (error, data) { // Show what's wrong if error if (error) return console.error(error); // Pull out values data.forEach(function (d) { d.date = parseDate(d.date); d.value = +d.value; }); // Get the maximum value in the given array var maxY = d3.max(data, function(d) { return d.value; }); // Reset start data for animation var startData = data.map(function(datum) { return { date: datum.date, value: 0 }; }); // Set input domains // ------------------------------ // Horizontal x.domain(d3.extent(data, function(d, i) { return d.date; })); // Vertical y.domain([0, d3.max( data, function(d) { return d.value; })]); // // Append chart elements // // Append axes // ------------------------- // Horizontal var horizontalAxis = svg.append('g') .attr('class', 'd3-axis d3-axis-horizontal') .attr('transform', 'translate(0,' + height + ')') .call(xAxis); // Add extra subticks for hidden hours horizontalAxis.selectAll('.d3-axis-subticks') .data(x.ticks(d3.time.days), function(d) { return d; }) .enter() .append('line') .attr('class', 'd3-axis-subticks') .attr('y1', 0) .attr('y2', 4) .attr('x1', x) .attr('x2', x); // Append area // ------------------------- // Add area path svg.append('path') .datum(data) .attr('class', 'd3-area') .attr('d', area) .style('fill', color) .transition() // begin animation .duration(1000) .attrTween('d', function() { var interpolator = d3.interpolateArray(startData, data); return function (t) { return area(interpolator (t)); } }); // Append crosshair and tooltip // ------------------------- // // Line // // Line group var focusLine = svg.append('g') .style('display', 'none'); // Line element focusLine.append('line') .attr('class', 'vertical-crosshair d3-crosshair-line') .attr('y1', 0) .attr('y2', -maxY); // // Pointer // // Pointer group var focusPointer = svg.append('g') .attr('class', 'd3-crosshair-pointer') .style('display', 'none'); // Pointer element focusPointer.append('circle') .attr('class', 'd3-line-circle') .attr('r', 3) .style('stroke', color) .style('stroke-width', 1.5); // // Text // // Text group var focusText = svg.append('g') .attr('class', 'd3-crosshair-text') .style('display', 'none'); // Text element focusText.append('text') .attr('class', 'd3-text') .attr('dy', -10) .style('font-size', 12); // // Overlay with events // svg.append('rect') .attr('class', 'd3-crosshair-overlay') .style('fill', 'none') .style('pointer-events', 'all') .attr('width', width) .attr('height', height) .on('mouseover', function() { focusPointer.style('display', null); focusLine.style('display', null) focusText.style('display', null); }) .on('mouseout', function() { focusPointer.style('display', 'none'); focusLine.style('display', 'none'); focusText.style('display', 'none'); }) .on('mousemove', mousemove); // Display tooltip on mousemove function mousemove() { // Define main variables var mouse = d3.mouse(this), mousex = mouse[0], mousey = mouse[1], x0 = x.invert(mousex), i = bisectDate(data, x0), d0 = data[i - 1], d1 = data[i], d = x0 - d0.date > d1.date - x0 ? d1 : d0; // Move line focusLine.attr('transform', 'translate(' + x(d.date) + ',' + height + ')'); // Move pointer focusPointer.attr('transform', 'translate(' + x(d.date) + ',' + y(d.value) + ')'); // Reverse tooltip at the end point if(mousex >= (d3Container.node().getBoundingClientRect().width - focusText.select('text').node().getBoundingClientRect().width - margin.right - margin.left)) { focusText.select('text').attr('text-anchor', 'end').attr('x', function () { return (x(d.date) - 15) + 'px' }).text(formatDate(d.date) + ' - ' + d.value + ' sales'); } else { focusText.select('text').attr('text-anchor', 'start').attr('x', function () { return (x(d.date) + 15) + 'px' }).text(formatDate(d.date) + ' - ' + d.value + ' sales'); } } // Resize chart // ------------------------------ // Call function on window resize window.addEventListener('resize', monthlySalesAreaResize); // Call function on sidebar width change var sidebarToggle = document.querySelector('.sidebar-control'); sidebarToggle && sidebarToggle.addEventListener('click', monthlySalesAreaResize); // Resize function // // Since D3 doesn't support SVG resize by default, // we need to manually specify parts of the graph that need to // be updated on window resize function monthlySalesAreaResize() { // Layout variables width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right; // Layout // ------------------------- // Main svg width container.attr('width', width + margin.left + margin.right); // Width of appended group svg.attr('width', width + margin.left + margin.right); // Axes // ------------------------- // Horizontal range x.range([0, width]); // Horizontal axis svg.selectAll('.d3-axis-horizontal').call(xAxis); // Horizontal axis subticks svg.selectAll('.d3-axis-subticks').attr('x1', x).attr('x2', x); // Chart elements // ------------------------- // Area path svg.selectAll('.d3-area').datum(data).attr('d', area); // Crosshair svg.selectAll('.d3-crosshair-overlay').attr('width', width); } }); } }; // Messages area chart var _MessagesAreaChart = function(element, height, color) { if (typeof d3 == 'undefined') { console.warn('Warning - d3.min.js is not loaded.'); return; } // Initialize chart only if element exsists in the DOM if($(element).length > 0) { // Basic setup // ------------------------------ // Define main variables var d3Container = d3.select(element), margin = {top: 0, right: 0, bottom: 0, left: 0}, width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right, height = height - margin.top - margin.bottom; // Date and time format var parseDate = d3.time.format('%Y-%m-%d').parse; // Create SVG // ------------------------------ // Container var container = d3Container.append('svg'); // SVG element var svg = container .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') // Construct chart layout // ------------------------------ // Area var area = d3.svg.area() .x(function(d) { return x(d.date); }) .y0(height) .y1(function(d) { return y(d.value); }) .interpolate('monotone') // Construct scales // ------------------------------ // Horizontal var x = d3.time.scale().range([0, width ]); // Vertical var y = d3.scale.linear().range([height, 0]); // Load data // ------------------------------ d3.json('../../../../global_assets/demo_data/dashboard/monthly_sales.json', function (error, data) { // Show what's wrong if error if (error) return console.error(error); // Pull out values data.forEach(function (d) { d.date = parseDate(d.date); d.value = +d.value; }); // Get the maximum value in the given array var maxY = d3.max(data, function(d) { return d.value; }); // Reset start data for animation var startData = data.map(function(datum) { return { date: datum.date, value: 0 }; }); // Set input domains // ------------------------------ // Horizontal x.domain(d3.extent(data, function(d, i) { return d.date; })); // Vertical y.domain([0, d3.max( data, function(d) { return d.value; })]); // // Append chart elements // // Add area path svg.append('path') .datum(data) .attr('class', 'd3-area') .style('fill', color) .attr('d', area) .transition() // begin animation .duration(1000) .attrTween('d', function() { var interpolator = d3.interpolateArray(startData, data); return function (t) { return area(interpolator (t)); } }); // Resize chart // ------------------------------ // Call function on window resize window.addEventListener('resize', messagesAreaResize); // Call function on sidebar width change var sidebarToggle = document.querySelector('.sidebar-control'); sidebarToggle && sidebarToggle.addEventListener('click', messagesAreaResize); // Resize function // // Since D3 doesn't support SVG resize by default, // we need to manually specify parts of the graph that need to // be updated on window resize function messagesAreaResize() { // Layout variables width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right; // Layout // ------------------------- // Main svg width container.attr('width', width + margin.left + margin.right); // Width of appended group svg.attr('width', width + margin.left + margin.right); // Horizontal range x.range([0, width]); // Chart elements // ------------------------- // Area path svg.selectAll('.d3-area').datum( data ).attr('d', area); } }); } }; // // Return objects assigned to module // return { init: function() { _MonthlySalesAreaChart('#monthly-sales-stats', 100, '#4DB6AC'); _MessagesAreaChart('#messages-stats', 40, '#5C6BC0'); } } }(); // Initialize module // ------------------------------ document.addEventListener('DOMContentLoaded', function() { DashboardAreas.init(); });