UNPKG

@spalger/kibana

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

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; }; });