UNPKG

nvd3-fork

Version:

FORK! of NVD3, a reusable charting library written in d3.js

405 lines (342 loc) 15.7 kB
nv.models.historicalBarChart = function(bar_model) { "use strict"; //============================================================ // Public Variables with Default Settings //------------------------------------------------------------ var bars = bar_model || nv.models.historicalBar() , xAxis = nv.models.axis() , yAxis = nv.models.axis() , legend = nv.models.legend() , interactiveLayer = nv.interactiveGuideline() , tooltip = nv.models.tooltip() ; var margin = {top: 30, right: 90, bottom: 50, left: 90} , marginTop = null , color = nv.utils.defaultColor() , width = null , height = null , showLegend = false , showXAxis = true , showYAxis = true , rightAlignYAxis = false , useInteractiveGuideline = false , x , y , state = {} , defaultState = null , noData = null , dispatch = d3.dispatch('tooltipHide', 'stateChange', 'changeState', 'renderEnd') , transitionDuration = 250 ; xAxis.orient('bottom').tickPadding(7); yAxis.orient( (rightAlignYAxis) ? 'right' : 'left'); tooltip .duration(0) .headerEnabled(false) .valueFormatter(function(d, i) { return yAxis.tickFormat()(d, i); }) .headerFormatter(function(d, i) { return xAxis.tickFormat()(d, i); }); //============================================================ // Private Variables //------------------------------------------------------------ var renderWatch = nv.utils.renderWatch(dispatch, 0); function chart(selection) { selection.each(function(data) { renderWatch.reset(); renderWatch.models(bars); if (showXAxis) renderWatch.models(xAxis); if (showYAxis) renderWatch.models(yAxis); var container = d3.select(this), that = this; nv.utils.initSVG(container); var availableWidth = nv.utils.availableWidth(width, container, margin), availableHeight = nv.utils.availableHeight(height, container, margin); chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; chart.container = this; //set state.disabled state.disabled = data.map(function(d) { return !!d.disabled }); if (!defaultState) { var key; defaultState = {}; for (key in state) { if (state[key] instanceof Array) defaultState[key] = state[key].slice(0); else defaultState[key] = state[key]; } } // Display noData message if there's nothing to show. if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) { nv.utils.noData(chart, container) return chart; } else { container.selectAll('.nv-noData').remove(); } // Setup Scales x = bars.xScale(); y = bars.yScale(); // Setup containers and skeleton of chart var wrap = container.selectAll('g.nv-wrap.nv-historicalBarChart').data([data]); var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-historicalBarChart').append('g'); var g = wrap.select('g'); gEnter.append('g').attr('class', 'nv-x nv-axis'); gEnter.append('g').attr('class', 'nv-y nv-axis'); gEnter.append('g').attr('class', 'nv-barsWrap'); gEnter.append('g').attr('class', 'nv-legendWrap'); gEnter.append('g').attr('class', 'nv-interactive'); // Legend if (!showLegend) { g.select('.nv-legendWrap').selectAll('*').remove(); } else { legend.width(availableWidth); g.select('.nv-legendWrap') .datum(data) .call(legend); if (!marginTop && legend.height() !== margin.top) { margin.top = legend.height(); availableHeight = nv.utils.availableHeight(height, container, margin); } wrap.select('.nv-legendWrap') .attr('transform', 'translate(0,' + (-margin.top) +')') } wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); if (rightAlignYAxis) { g.select(".nv-y.nv-axis") .attr("transform", "translate(" + availableWidth + ",0)"); } //Set up interactive layer if (useInteractiveGuideline) { interactiveLayer .width(availableWidth) .height(availableHeight) .margin({left:margin.left, top:margin.top}) .svgContainer(container) .xScale(x); wrap.select(".nv-interactive").call(interactiveLayer); } bars .width(availableWidth) .height(availableHeight) .color(data.map(function(d,i) { return d.color || color(d, i); }).filter(function(d,i) { return !data[i].disabled })); var barsWrap = g.select('.nv-barsWrap') .datum(data.filter(function(d) { return !d.disabled })); barsWrap.transition().call(bars); // Setup Axes if (showXAxis) { xAxis .scale(x) ._ticks( nv.utils.calcTicksX(availableWidth/100, data) ) .tickSize(-availableHeight, 0); g.select('.nv-x.nv-axis') .attr('transform', 'translate(0,' + y.range()[0] + ')'); g.select('.nv-x.nv-axis') .transition() .call(xAxis); } if (showYAxis) { yAxis .scale(y) ._ticks( nv.utils.calcTicksY(availableHeight/36, data) ) .tickSize( -availableWidth, 0); g.select('.nv-y.nv-axis') .transition() .call(yAxis); } //============================================================ // Event Handling/Dispatching (in chart's scope) //------------------------------------------------------------ interactiveLayer.dispatch.on('elementMousemove', function(e) { bars.clearHighlights(); var singlePoint, pointIndex, pointXLocation, allData = []; data .filter(function(series, i) { series.seriesIndex = i; return !series.disabled; }) .forEach(function(series,i) { pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x()); bars.highlightPoint(pointIndex,true); var point = series.values[pointIndex]; if (point === undefined) return; if (singlePoint === undefined) singlePoint = point; if (pointXLocation === undefined) pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); allData.push({ key: series.key, value: chart.y()(point, pointIndex), color: color(series,series.seriesIndex), data: series.values[pointIndex] }); }); var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex)); interactiveLayer.tooltip .valueFormatter(function(d,i) { return yAxis.tickFormat()(d); }) .data({ value: xValue, index: pointIndex, series: allData })(); interactiveLayer.renderGuideLine(pointXLocation); }); interactiveLayer.dispatch.on("elementMouseout",function(e) { dispatch.tooltipHide(); bars.clearHighlights(); }); legend.dispatch.on('legendClick', function(d,i) { d.disabled = !d.disabled; if (!data.filter(function(d) { return !d.disabled }).length) { data.map(function(d) { d.disabled = false; wrap.selectAll('.nv-series').classed('disabled', false); return d; }); } state.disabled = data.map(function(d) { return !!d.disabled }); dispatch.stateChange(state); selection.transition().call(chart); }); legend.dispatch.on('legendDblclick', function(d) { //Double clicking should always enable current series, and disabled all others. data.forEach(function(d) { d.disabled = true; }); d.disabled = false; state.disabled = data.map(function(d) { return !!d.disabled }); dispatch.stateChange(state); chart.update(); }); dispatch.on('changeState', function(e) { if (typeof e.disabled !== 'undefined') { data.forEach(function(series,i) { series.disabled = e.disabled[i]; }); state.disabled = e.disabled; } chart.update(); }); }); renderWatch.renderEnd('historicalBarChart immediate'); return chart; } //============================================================ // Event Handling/Dispatching (out of chart's scope) //------------------------------------------------------------ bars.dispatch.on('elementMouseover.tooltip', function(evt) { evt['series'] = { key: chart.x()(evt.data), value: chart.y()(evt.data), color: evt.color }; tooltip.data(evt).hidden(false); }); bars.dispatch.on('elementMouseout.tooltip', function(evt) { tooltip.hidden(true); }); bars.dispatch.on('elementMousemove.tooltip', function(evt) { tooltip(); }); //============================================================ // Expose Public Variables //------------------------------------------------------------ // expose chart's sub-components chart.dispatch = dispatch; chart.bars = bars; chart.legend = legend; chart.xAxis = xAxis; chart.yAxis = yAxis; chart.interactiveLayer = interactiveLayer; chart.tooltip = tooltip; chart.options = nv.utils.optionsFunc.bind(chart); chart._options = Object.create({}, { // simple options, just get/set the necessary values width: {get: function(){return width;}, set: function(_){width=_;}}, height: {get: function(){return height;}, set: function(_){height=_;}}, showLegend: {get: function(){return showLegend;}, set: function(_){showLegend=_;}}, showXAxis: {get: function(){return showXAxis;}, set: function(_){showXAxis=_;}}, showYAxis: {get: function(){return showYAxis;}, set: function(_){showYAxis=_;}}, defaultState: {get: function(){return defaultState;}, set: function(_){defaultState=_;}}, noData: {get: function(){return noData;}, set: function(_){noData=_;}}, // options that require extra logic in the setter margin: {get: function(){return margin;}, set: function(_){ if (_.top !== undefined) { margin.top = _.top; marginTop = _.top; } margin.right = _.right !== undefined ? _.right : margin.right; margin.bottom = _.bottom !== undefined ? _.bottom : margin.bottom; margin.left = _.left !== undefined ? _.left : margin.left; }}, color: {get: function(){return color;}, set: function(_){ color = nv.utils.getColor(_); legend.color(color); bars.color(color); }}, duration: {get: function(){return transitionDuration;}, set: function(_){ transitionDuration=_; renderWatch.reset(transitionDuration); yAxis.duration(transitionDuration); xAxis.duration(transitionDuration); }}, rightAlignYAxis: {get: function(){return rightAlignYAxis;}, set: function(_){ rightAlignYAxis = _; yAxis.orient( (_) ? 'right' : 'left'); }}, useInteractiveGuideline: {get: function(){return useInteractiveGuideline;}, set: function(_){ useInteractiveGuideline = _; if (_ === true) { chart.interactive(false); } }} }); nv.utils.inheritOptions(chart, bars); nv.utils.initOptions(chart); return chart; }; // ohlcChart is just a historical chart with ohlc bars and some tweaks nv.models.ohlcBarChart = function() { var chart = nv.models.historicalBarChart(nv.models.ohlcBar()); // special default tooltip since we show multiple values per x chart.useInteractiveGuideline(true); chart.interactiveLayer.tooltip.contentGenerator(function(data) { // we assume only one series exists for this chart var d = data.series[0].data; // match line colors as defined in nv.d3.css var color = d.open < d.close ? "2ca02c" : "d62728"; return '' + '<h3 style="color: #' + color + '">' + data.value + '</h3>' + '<table>' + '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' + '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' + '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' + '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' + '</table>'; }); return chart; }; // candlestickChart is just a historical chart with candlestick bars and some tweaks nv.models.candlestickBarChart = function() { var chart = nv.models.historicalBarChart(nv.models.candlestickBar()); // special default tooltip since we show multiple values per x chart.useInteractiveGuideline(true); chart.interactiveLayer.tooltip.contentGenerator(function(data) { // we assume only one series exists for this chart var d = data.series[0].data; // match line colors as defined in nv.d3.css var color = d.open < d.close ? "2ca02c" : "d62728"; return '' + '<h3 style="color: #' + color + '">' + data.value + '</h3>' + '<table>' + '<tr><td>open:</td><td>' + chart.yAxis.tickFormat()(d.open) + '</td></tr>' + '<tr><td>close:</td><td>' + chart.yAxis.tickFormat()(d.close) + '</td></tr>' + '<tr><td>high</td><td>' + chart.yAxis.tickFormat()(d.high) + '</td></tr>' + '<tr><td>low:</td><td>' + chart.yAxis.tickFormat()(d.low) + '</td></tr>' + '</table>'; }); return chart; };