UNPKG

@domoinc/multiline-chart

Version:

MultiLineChart - Domo Widget

618 lines (536 loc) 20.3 kB
/*! Copyright 2016 Domo Inc. */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(require("d3"), require("d3.chart"), require("@domoinc/base-widget"), require("@domoinc/domo-tooltip"), require("@domoinc/da-theme2"), require("@domoinc/summary-number"), require("@domoinc/utilities"), require("lodash")); else if(typeof define === 'function' && define.amd) define(["d3", "d3.chart", "base-widget", "domo-tooltip", "da-theme2", "summary-number", "utilities", "lodash"], factory); else if(typeof exports === 'object') exports["Axis"] = factory(require("d3"), require("d3.chart"), require("@domoinc/base-widget"), require("@domoinc/domo-tooltip"), require("@domoinc/da-theme2"), require("@domoinc/summary-number"), require("@domoinc/utilities"), require("lodash")); else root["Axis"] = factory(root["d3"], root["d3.chart"], root["BaseWidget"], root["DomoTooltip"], root["da"]["theme2"], root["SummaryNumber"], root["da"], root["_"]); })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__, __WEBPACK_EXTERNAL_MODULE_4__, __WEBPACK_EXTERNAL_MODULE_5__, __WEBPACK_EXTERNAL_MODULE_6__, __WEBPACK_EXTERNAL_MODULE_7__, __WEBPACK_EXTERNAL_MODULE_8__) { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = "/dist/"; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { var d3 = __webpack_require__(1); var d3Chart = __webpack_require__(2); var BaseWidget = __webpack_require__(3); var DomoTooltip = __webpack_require__(4); var daTheme2 = __webpack_require__(5); var SummaryNumber = __webpack_require__(6); var Utilities = __webpack_require__(7); var _ = __webpack_require__(8); //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- // Axis //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- module.exports = BaseWidget.extend('Axis', { //********************************************************************************** // Anything in the initialization method will only run once. // i.e. It will not be run every time you call draw. //********************************************************************************** initialize: function () { 'use strict'; var _Chart = this; var _scale = d3.scale.linear(); //---------------------------------------------------------------------------------- // Default Values for the wiget's configurable options. // Valid types for UI elements are: string, number, color, select, boolean // NOTE: Height and Width have already been set on the base widget. // // Valid Properties: [Name], description, [category], type, value, [onChange] //---------------------------------------------------------------------------------- this._newConfig = { addBaseline: { description: 'Adds the baseline path above the labels', type: 'boolean', value: false, }, addZeroline: { description: 'Makes the gridline corresponding to the zero tick show, and have increased stroke width', type: 'boolean', value: false, }, addGridlines: { description: 'Flag to show vertical or horizontal grid lines.', type: 'boolean', value: false, }, addTicks: { description: 'Flag to show the tick lines on the axis.', type: 'boolean', value: false }, addLabels: { description: 'Flag to show the text labels on the axis', type: 'boolean', value: true, }, intelligentTicks: { description: 'Try and show an appropriate number of ticks given available space.', type: 'boolean', value: true }, intelligentTrunc: { description: 'Truncate the labels so they do not over lap.', type: 'boolean', value: true }, maxLegendSpace: { description: '', type: 'number', value: 50 }, orient: { description: 'Sets the orientation of the axis. Options: bottom, top, left, right', type: 'string', value: 'bottom' }, scale: { description: 'd3 scale object for the axis.', type: 'scale', value: _scale, }, tickFormat: { description: 'Label format function for the tick labels.', type: 'function', value: function (d) { return service.summaryNumber(d); } }, tickPadding: { description: 'Padding between the tick line and text.', type: 'number', value: 7 }, tickSize: { description: 'Size of the tick line.', type: 'number', value: 5 }, tickSpacing: { description: 'Space between each tick.', type: 'number', value: 40 }, axesLabelLetterSpacing: { description: 'Letter spacing for the axes labels.', type: 'number', value: 0, units: 'px' }, x: { description: 'The x coordinate for the axis.', type: 'number', value: 0 }, y: { description: 'The y coordinate for the axis.', type: 'number', value: 0 }, duration: { description: 'Duration of the animation', value: 750, type: 'number', units: 'ms', }, textFontFamily: daTheme2.themeElements.h1FontFamily({ name: 'Text Font Family', description: '', }), tooltipBackgroundColor: daTheme2.themeElements.tooltipBackground(), tooltipTextSize: { name: 'Tooltip Text Size', value: 14, units: 'px', type: 'number', }, tooltipTextColor: daTheme2.themeElements.tooltipFontColor(), showTooltip: { value: false, type: 'boolean', }, chartName: { description: 'Name of chart for Reporting.', type: 'string', value: 'Axis' }, //THEME Elements /*---------------------------------------------------------------------------------- //Axes (4): ----------------------------------------------------------------------------------*/ axesLineColor: daTheme2.themeElements.axesLineColor(), axesLabelColor: daTheme2.themeElements.axesFontColor(), axesLabelSize: daTheme2.themeElements.axesFontSize(), axesLabelFontFamily: daTheme2.themeElements.axesFontFamily() }; // Update config object from base chart. this.mergeConfig(_Chart._newConfig); //---------------------------------------------------------------------------------- // Static - Anything that is defined here will never change //---------------------------------------------------------------------------------- var service = new SummaryNumber(); _Chart._axisBase = _Chart._layerGroup.classed('axis', true); _Chart._axis = d3.svg.axis(); _Chart._svg = findSvg(_Chart._layerGroup.node()); function findSvg(selection) { var selectionParent = selection.parentNode; if (selectionParent.tagName === 'svg') { return d3.select(selectionParent); } else { return findSvg(selectionParent); } } var tooltip = _Chart._svg.append('g') .classed('axisTooltip', true) .chart('DomoTooltip') .c('format', 'text') .c('container', _Chart._svg); tooltip._appends.append('text').classed('axisLabel', true); /*---------------------------------------------------------------------------------- // Transform ----------------------------------------------------------------------------------*/ this.transform = function (data) { updateTooltip(); verifyOreintation(); if (_Chart.c('scale') === _scale) { setUpDefaultScale(data); } drawAxis(); return data; }; //---------------------------------------------------------------------------------- // Helper Functions //---------------------------------------------------------------------------------- /** * Verifies orientation of the axis 'orient' config. * Sets config to 'bottom' of not a valid config. */ function verifyOreintation() { var validOptions = [ 'top', // horizontal axis with ticks above the domain path 'bottom', // horizontal axis with ticks below the domain path 'left', // vertical axis with ticks to the left of the domain path 'right', // vertical axis with ticks to the right of the domain path ]; if (_.indexOf(validOptions, _Chart.c('orient')) < 0) { console.warn('Not a valid option for orient property.' + 'Valid options are "bottom", "top", "left", "right".'); _Chart.c('orient', 'bottom'); } } /** * Sets Data Scale * @param data */ function setUpDefaultScale(data) { _Chart.c('scale').domain([0, d3.max(data)]); if (_Chart.c('orient') === 'bottom' || _Chart.c('orient') === 'top') { _Chart.c('scale').range([0, _Chart.c('width')]); } else { _Chart.c('scale').range([_Chart.c('height'), 0]); } } /** * DrawAxis */ function drawAxis() { setupAxis(); _Chart._axisBase.transition().duration(_Chart.c('duration')).call(_Chart._axis); styleAxis(); } /** * SetupAxis */ function setupAxis() { _Chart._axisBase.attr('transform', 'translate(' + _Chart.c('x') + ',' + _Chart.c('y') + ')'); _Chart._axis .scale(_Chart.c('scale')) .tickFormat(_Chart.c('tickFormat')) .tickSize(_Chart.c('tickSize')) .tickPadding(_Chart.c('tickPadding')) .orient(_Chart.c('orient')); if (_Chart.c('intelligentTicks')) { intelligentTicksNumTicks(); } if (!_Chart.c('addTicks')) { _Chart._axis.tickSize(0); } //if you don't add gridlines, tick will position and //be sized automatically by tickSize option on _Chart._axis if (_Chart.c('addGridlines')) { var size = getTickSize(); _Chart._axis.innerTickSize(-size); _Chart._axis.outerTickSize(0); } } /** * get the tick size depending if gridlines are added * and axis orientation */ function getTickSize() { var size = _Chart.c('width'); if (_Chart.c('orient') === 'bottom' || _Chart.c('orient') === 'top') { size = _Chart.c('height'); } if (_Chart.c('addTicks')) { size += _Chart.c('tickSize'); } return size; } /** * get tick transform depending on axis orientation * this function is only needed if there is ticks and gridlines */ function getTickTransform() { var tickSize = _Chart.c('tickSize'); switch (_Chart.c('orient')) { case 'top': return 'translate(0,-' + tickSize + ')'; case 'bottom': return 'translate(0,' + tickSize + ')'; case 'left': return 'translate(-' + tickSize + ',0)'; case 'right': return 'translate(' + tickSize + ',0)'; } } /** * StyleAxis */ function styleAxis() { _Chart._axisBase.selectAll('path') .style({ 'fill': 'none', 'stroke': _Chart.c('addBaseline') ? _Chart.c('axesLineColor') : 'none', 'stroke-width': 2, }); _Chart._axisBase.selectAll('line') .attr({ transform: function(){ if (_Chart.c('addTicks') && _Chart.c('addGridlines')) { return getTickTransform(); } } }) .style('stroke', _Chart.c('axesLineColor')) .each(function(d) { // Add zeroline var elem; if (d === 0 && _Chart.c('addZeroline')) { elem = d3.select(this); elem.style('stroke-width', 2); if (_Chart.c('addTicks')){ elem.attr('transform', getTickTransform); } if (_Chart.c('orient') === 'top') { elem.transition().attr({ 'y2': getTickSize(), 'x2': 0, }); } else if (_Chart.c('orient') === 'bottom'){ elem.transition().attr({ 'y2': -getTickSize(), 'x2': 0, }); } else if (_Chart.c('orient') === 'left'){ elem.transition().attr({ 'x2': getTickSize(), 'y2': 0, }); } else if (_Chart.c('orient') === 'right'){ elem.transition().attr({ 'x2': -getTickSize(), 'y2': 0, }); } } else if (d === 0 && !_Chart.c('addZeroline')) { // Remove zeroline elem = d3.select(this); elem.style('stroke-width', 1); } }); _Chart._axisBase.selectAll('text') .on('mousemove', hoverMove) .on('mouseout', hoverOff) .style({ 'fill': _Chart.c('axesLabelColor'), 'font-family': _Chart.c('axesLabelFontFamily'), 'font-weight': 400, 'font-size': _Chart.c('addLabels') ? _Chart.c('axesLabelSize') + 'px' : 0.1 + 'px', 'letter-spacing': _Chart.c('axesLabelLetterSpacing') + 'px' }); if (_Chart.c('intelligentTicks') || !_Chart.c('intelligentTrunc')) { intelligentTicksTruncate(); } } /** * Tries to set an appropriate number of ticks given chart space. */ function intelligentTicksNumTicks() { var numTicks = Math.round(getLengthOfAxis() / _Chart.c('tickSpacing')); _Chart._axis.ticks(numTicks); setTicksForPossibleOrdinalScale(numTicks); } //********************************************************************************** // Ordinal scale do not respond to the 'ticks' function on a scale. //********************************************************************************** function setTicksForPossibleOrdinalScale(numTicks) { //Most likely an ordinal scale... try tickValues instead if (_Chart.c('scale').hasOwnProperty('rangePoints')) { var domain = _Chart.c('scale').domain(); _Chart._axis.tickValues(domain); // ensure the tickValues are always set to the recent domain values if (numTicks < domain.length) { var showMod = Math.round(domain.length / numTicks); var showTicks = _.filter(domain, function (d, i) { return i % showMod === 0; }); var lastDomain = domain[domain.length - 1]; var lastShown = showTicks[showTicks.length - 1]; //Last Tick Conditional: Fix the Last tick. Show one / move it to the last number in the domain. if (domain.length - (showTicks.length * showMod) > showMod * 0.50) { showTicks.push(lastDomain); } else { showTicks[showTicks.length - 1] = lastDomain; } _Chart._axis.tickValues(showTicks); } } } /** * Truncates tick labels to that they don't overlay. */ function intelligentTicksTruncate() { var unattachedAxis = d3.select(document.createElement('g')).call(_Chart._axis); var unattachedTextCnt = unattachedAxis.selectAll('text').size(); var orient = _Chart.c('orient'); var trueSpacing = orient === 'left' || orient === 'right' ? _Chart.c('maxLegendSpace') : Math.round(getLengthOfAxis() / unattachedTextCnt); _Chart._axisBase.selectAll('text') .each(function (d) { d3.domoStrings.truncToFit(d3.select(this), trueSpacing); }); } /** * Returns length of axis given the current orientation. */ function getLengthOfAxis() { var orient = _Chart.c('orient'); return orient === 'bottom' || orient === 'top' ? _Chart.c('width') : _Chart.c('height'); } /** * hover move */ function hoverMove(d) { if (_Chart.c('showTooltip')) { var valueIsString = !parseFloat(d); //if it's a string, and not a string number //if the string data is different from the text var dataIsDifferentFromText = String(d) !== d3.select(this).text(); // jshint ignore:line var labelIsTruncated = valueIsString && dataIsDifferentFromText; if (labelIsTruncated) { var coordinates = d3.mouse(_Chart._svg.node()); var x = coordinates[0]; var y = coordinates[1]; var point = { x: x, y: y, }; tooltip._appends.select('text').text(function() { return _Chart.c('tickFormat')(d); }) tooltip.trigger('draw'); tooltip.trigger('moveTo', point) } } } /** * hover off */ function hoverOff(d) { if (_Chart.c('showTooltip')) { tooltip.trigger('remove'); } } /** * update tooltip */ function updateTooltip() { tooltip._appends.selectAll('text') .style({ 'font-family': _Chart.c('textFontFamily'), 'font-size': _Chart.c('tooltipTextSize') + 'px', 'fill': _Chart.c('tooltipTextColor'), }); tooltip._appends.select('rect').style('fill', _Chart.c('tooltipTextColor')); tooltip.c('tooltipBackgroundColor', _Chart.c('tooltipBackgroundColor')) } } }); /***/ }, /* 1 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_1__; /***/ }, /* 2 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_2__; /***/ }, /* 3 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_3__; /***/ }, /* 4 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_4__; /***/ }, /* 5 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_5__; /***/ }, /* 6 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_6__; /***/ }, /* 7 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_7__; /***/ }, /* 8 */ /***/ function(module, exports) { module.exports = __WEBPACK_EXTERNAL_MODULE_8__; /***/ } /******/ ]) }); ;