UNPKG

kibana-with-account

Version:

kibana-with-account is develop based kibana project

329 lines (281 loc) 9.3 kB
define(function (require) { return function ColumnChartFactory(Private) { var d3 = require('d3'); var _ = require('lodash'); var $ = require('jquery'); var moment = require('moment'); var DataClass = Private(require('ui/vislib/lib/data')); var PointSeriesChart = Private(require('ui/vislib/visualizations/_point_series_chart')); var TimeMarker = Private(require('ui/vislib/visualizations/time_marker')); var errors = require('ui/errors'); /** * Vertical Bar Chart Visualization: renders vertical and/or stacked bars * * @class ColumnChart * @constructor * @extends Chart * @param handler {Object} Reference to the Handler Class Constructor * @param el {HTMLElement} HTML element to which the chart will be appended * @param chartData {Object} Elasticsearch query results for this specific chart */ _.class(ColumnChart).inherits(PointSeriesChart); function ColumnChart(handler, chartEl, chartData) { if (!(this instanceof ColumnChart)) { return new ColumnChart(handler, chartEl, chartData); } ColumnChart.Super.apply(this, arguments); // Column chart specific attributes this._attr = _.defaults(handler._attr || {}, { xValue: function (d) { return d.x; }, yValue: function (d) { return d.y; } }); } /** * Adds SVG rect to Vertical Bar Chart * * @method addBars * @param svg {HTMLElement} SVG to which rect are appended * @param layers {Array} Chart data array * @returns {D3.UpdateSelection} SVG with rect added */ ColumnChart.prototype.addBars = function (svg, layers) { var self = this; var color = this.handler.data.getColorFunc(); var tooltip = this.tooltip; var isTooltip = this._attr.addTooltip; var layer; var bars; layer = svg.selectAll('.layer') .data(layers) .enter().append('g') .attr('class', function (d, i) { return 'series ' + i; }); bars = layer.selectAll('rect') .data(function (d) { return d; }); bars .exit() .remove(); bars .enter() .append('rect') .call(this._addIdentifier) .attr('fill', function (d) { return color(d.label); }); self.updateBars(bars); // Add tooltip if (isTooltip) { bars.call(tooltip.render()); } return bars; }; /** * Determines whether bars are grouped or stacked and updates the D3 * selection * * @method updateBars * @param bars {D3.UpdateSelection} SVG with rect added * @returns {D3.UpdateSelection} */ ColumnChart.prototype.updateBars = function (bars) { var offset = this._attr.mode; if (offset === 'grouped') { return this.addGroupedBars(bars); } return this.addStackedBars(bars); }; /** * Adds stacked bars to column chart visualization * * @method addStackedBars * @param bars {D3.UpdateSelection} SVG with rect added * @returns {D3.UpdateSelection} */ ColumnChart.prototype.addStackedBars = function (bars) { var data = this.chartData; var xScale = this.handler.xAxis.xScale; var yScale = this.handler.yAxis.yScale; var height = yScale.range()[0]; var yMin = this.handler.yAxis.yScale.domain()[0]; var self = this; var barWidth; if (data.ordered && data.ordered.date) { var start = data.ordered.min; var end = moment(data.ordered.min).add(data.ordered.interval).valueOf(); barWidth = xScale(end) - xScale(start); barWidth = barWidth - Math.min(barWidth * 0.25, 15); } // update bars .attr('x', function (d) { return xScale(d.x); }) .attr('width', function () { return barWidth || xScale.rangeBand(); }) .attr('y', function (d) { if (d.y < 0) { return yScale(d.y0); } return yScale(d.y0 + d.y); }) .attr('height', function (d) { if (d.y < 0) { return Math.abs(yScale(d.y0 + d.y) - yScale(d.y0)); } // Due to an issue with D3 not returning zeros correctly when using // an offset='expand', need to add conditional statement to handle zeros // appropriately if (d._input.y === 0) { return 0; } // for split bars or for one series, // last series will have d.y0 = 0 if (d.y0 === 0 && yMin > 0) { return yScale(yMin) - yScale(d.y); } return yScale(d.y0) - yScale(d.y0 + d.y); }); return bars; }; /** * Adds grouped bars to column chart visualization * * @method addGroupedBars * @param bars {D3.UpdateSelection} SVG with rect added * @returns {D3.UpdateSelection} */ ColumnChart.prototype.addGroupedBars = function (bars) { var xScale = this.handler.xAxis.xScale; var yScale = this.handler.yAxis.yScale; var yMin = this.handler.yAxis.yMin; var data = this.chartData; var n = data.series.length; var height = yScale.range()[0]; var groupSpacingPercentage = 0.15; var isTimeScale = (data.ordered && data.ordered.date); var minWidth = 1; var barWidth; // update bars .attr('x', function (d, i, j) { if (isTimeScale) { var groupWidth = xScale(data.ordered.min + data.ordered.interval) - xScale(data.ordered.min); var groupSpacing = groupWidth * groupSpacingPercentage; barWidth = (groupWidth - groupSpacing) / n; return xScale(d.x) + barWidth * j; } return xScale(d.x) + xScale.rangeBand() / n * j; }) .attr('width', function () { if (barWidth < minWidth) { throw new errors.ContainerTooSmall(); } if (isTimeScale) { return barWidth; } return xScale.rangeBand() / n; }) .attr('y', function (d) { if (d.y < 0) { return yScale(0); } return yScale(d.y); }) .attr('height', function (d) { return Math.abs(yScale(0) - yScale(d.y)); }); return bars; }; /** * Adds Events to SVG rect * Visualization is only brushable when a brush event is added * If a brush event is added, then a function should be returned. * * @method addBarEvents * @param element {D3.UpdateSelection} target * @param svg {D3.UpdateSelection} chart SVG * @returns {D3.Selection} rect with event listeners attached */ ColumnChart.prototype.addBarEvents = function (element, svg) { var events = this.events; var isBrushable = events.isBrushable(); var brush = isBrushable ? events.addBrushEvent(svg) : undefined; var hover = events.addHoverEvent(); var mouseout = events.addMouseoutEvent(); var click = events.addClickEvent(); var attachedEvents = element.call(hover).call(mouseout).call(click); if (isBrushable) { attachedEvents.call(brush); } return attachedEvents; }; /** * Renders d3 visualization * * @method draw * @returns {Function} Creates the vertical bar chart */ ColumnChart.prototype.draw = function () { var self = this; var $elem = $(this.chartEl); var margin = this._attr.margin; var elWidth = this._attr.width = $elem.width(); var elHeight = this._attr.height = $elem.height(); var yMin = this.handler.yAxis.yMin; var yScale = this.handler.yAxis.yScale; var xScale = this.handler.xAxis.xScale; var minWidth = 20; var minHeight = 20; var addTimeMarker = this._attr.addTimeMarker; var times = this._attr.times || []; var timeMarker; var div; var svg; var width; var height; var layers; var bars; return function (selection) { selection.each(function (data) { layers = self.stackData(data); width = elWidth; height = elHeight - margin.top - margin.bottom; if (addTimeMarker) { timeMarker = new TimeMarker(times, xScale, height); } if (width < minWidth || height < minHeight) { throw new errors.ContainerTooSmall(); } div = d3.select(this); svg = div.append('svg') .attr('width', width) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', 'translate(0,' + margin.top + ')'); bars = self.addBars(svg, layers); self.createEndZones(svg); // Adds event listeners self.addBarEvents(bars, svg); var line = svg.append('line') .attr('class', 'base-line') .attr('x1', 0) .attr('y1', yScale(0)) .attr('x2', width) .attr('y2', yScale(0)) .style('stroke', '#ddd') .style('stroke-width', 1); if (addTimeMarker) { timeMarker.render(svg); } return svg; }); }; }; return ColumnChart; }; });