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

237 lines (199 loc) 7.28 kB
define(function (require) { return function YAxisFactory(Private) { var d3 = require('d3'); var _ = require('lodash'); var $ = require('jquery'); var errors = require('ui/errors'); var ErrorHandler = Private(require('ui/vislib/lib/_error_handler')); /** * Appends y axis to the visualization * * @class YAxis * @constructor * @param args {{el: (HTMLElement), yMax: (Number), _attr: (Object|*)}} */ _.class(YAxis).inherits(ErrorHandler); function YAxis(args) { this.el = args.el; this.scale = null; this.domain = [args.yMin, args.yMax]; this.yAxisFormatter = args.yAxisFormatter; this._attr = args._attr || {}; } /** * Renders the y axis * * @method render * @return {D3.UpdateSelection} Renders y axis to visualization */ YAxis.prototype.render = function () { d3.select(this.el).selectAll('.y-axis-div').call(this.draw()); }; YAxis.prototype._isPercentage = function () { return (this._attr.mode === 'percentage'); }; YAxis.prototype._isUserDefined = function () { return (this._attr.setYExtents); }; YAxis.prototype._isYExtents = function () { return (this._attr.defaultYExtents); }; YAxis.prototype._validateUserExtents = function (domain) { var self = this; return domain.map(function (val) { val = parseInt(val, 10); if (isNaN(val)) throw new Error(val + ' is not a valid number'); if (self._isPercentage() && self._attr.setYExtents) return val / 100; return val; }); }; YAxis.prototype._getExtents = function (domain) { var min = domain[0]; var max = domain[1]; if (this._isUserDefined()) return this._validateUserExtents(domain); if (this._isYExtents()) return domain; if (this._attr.scale === 'log') return this._logDomain(min, max); // Negative values cannot be displayed with a log scale. if (!this._isYExtents() && !this._isUserDefined()) return [Math.min(0, min), Math.max(0, max)]; return domain; }; YAxis.prototype._throwCustomError = function (message) { throw new Error(message); }; YAxis.prototype._throwLogScaleValuesError = function () { throw new errors.InvalidLogScaleValues(); }; /** * Returns the appropriate D3 scale * * @param fnName {String} D3 scale * @returns {*} */ YAxis.prototype._getScaleType = function (fnName) { if (fnName === 'square root') fnName = 'sqrt'; // Rename 'square root' to 'sqrt' fnName = fnName || 'linear'; if (typeof d3.scale[fnName] !== 'function') return this._throwCustomError('YAxis.getScaleType: ' + fnName + ' is not a function'); return d3.scale[fnName](); }; /** * Return the domain for log scale, i.e. the extent of the log scale. * Log scales must begin at 1 since the log(0) = -Infinity * * @param scale * @param yMin * @param yMax * @returns {*[]} */ YAxis.prototype._logDomain = function (min, max) { if (min < 0 || max < 0) return this._throwLogScaleValuesError(); return [1, max]; }; /** * Creates the d3 y scale function * * @method getYScale * @param height {Number} DOM Element height * @returns {D3.Scale.QuantitiveScale|*} D3 yScale function */ YAxis.prototype.getYScale = function (height) { var scale = this._getScaleType(this._attr.scale); var domain = this._getExtents(this.domain); this.yScale = scale .domain(domain) .range([height, 0]); if (!this._isUserDefined()) this.yScale.nice(); // round extents when not user defined // Prevents bars from going off the chart when the y extents are within the domain range if (this._attr.type === 'histogram') this.yScale.clamp(true); return this.yScale; }; YAxis.prototype.getScaleType = function () { return this._attr.scale; }; YAxis.prototype.tickFormat = function () { var isPercentage = this._attr.mode === 'percentage'; if (isPercentage) return d3.format('%'); if (this.yAxisFormatter) return this.yAxisFormatter; return d3.format('n'); }; YAxis.prototype._validateYScale = function (yScale) { if (!yScale || _.isNaN(yScale)) throw new Error('yScale is ' + yScale); }; /** * Creates the d3 y axis function * * @method getYAxis * @param height {Number} DOM Element height * @returns {D3.Svg.Axis|*} D3 yAxis function */ YAxis.prototype.getYAxis = function (height) { var yScale = this.getYScale(height); this._validateYScale(yScale); // Create the d3 yAxis function this.yAxis = d3.svg.axis() .scale(yScale) .tickFormat(this.tickFormat(this.domain)) .ticks(this.tickScale(height)) .orient('left'); return this.yAxis; }; /** * Create a tick scale for the y axis that modifies the number of ticks * based on the height of the wrapping DOM element * Avoid using even numbers in the yTickScale.range * Causes the top most tickValue in the chart to be missing * * @method tickScale * @param height {Number} DOM element height * @returns {number} Number of y axis ticks */ YAxis.prototype.tickScale = function (height) { var yTickScale = d3.scale.linear() .clamp(true) .domain([20, 40, 1000]) .range([0, 3, 11]); return Math.ceil(yTickScale(height)); }; /** * Renders the y axis to the visualization * * @method draw * @returns {Function} Renders y axis to visualization */ YAxis.prototype.draw = function () { var self = this; var margin = this._attr.margin; var mode = this._attr.mode; var isWiggleOrSilhouette = (mode === 'wiggle' || mode === 'silhouette'); return function (selection) { selection.each(function () { var el = this; var div = d3.select(el); var width = $(el).parent().width(); var height = $(el).height(); var adjustedHeight = height - margin.top - margin.bottom; // Validate whether width and height are not 0 or `NaN` self.validateWidthandHeight(width, adjustedHeight); var yAxis = self.getYAxis(adjustedHeight); // The yAxis should not appear if mode is set to 'wiggle' or 'silhouette' if (!isWiggleOrSilhouette) { // Append svg and y axis var svg = div.append('svg') .attr('width', width) .attr('height', height); svg.append('g') .attr('class', 'y axis') .attr('transform', 'translate(' + (width - 2) + ',' + margin.top + ')') .call(yAxis); var container = svg.select('g.y.axis').node(); if (container) { var cWidth = Math.max(width, container.getBBox().width); svg.attr('width', cWidth); svg.select('g') .attr('transform', 'translate(' + (cWidth - 2) + ',' + margin.top + ')'); } } }); }; }; return YAxis; }; });