UNPKG

@domoinc/base-widget

Version:

BaseWidget - Domo Widget

527 lines (436 loc) 16.2 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/notifier"), require("lodash")); else if(typeof define === 'function' && define.amd) define(["d3", "d3.chart", "notifier", "lodash"], factory); else if(typeof exports === 'object') exports["BaseWidget"] = factory(require("d3"), require("d3.chart"), require("@domoinc/notifier"), require("lodash")); else root["BaseWidget"] = factory(root["d3"], root["d3.chart"], root["Notifier"], root["_"]); })(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__, __WEBPACK_EXTERNAL_MODULE_4__) { 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__) { 'use strict'; var d3 = __webpack_require__(1); var d3Chart = __webpack_require__(2); var Notifier = __webpack_require__(3); var _ = __webpack_require__(4); //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- // Base Widget: // This widget doesn't do anything by it self but it contains all the "base" // features that our widgets need. This widget should be "extended" to do anthing that // you need it to do. //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- function roundToOneDecimal(x) { return Math.round(x * 10) / 10; } function createGroups(base, isSvg) { var groups = {}; if (isSvg) { groups.container = base; groups.layer = base.append('g'); groups.notifier = base.append('g'); } else { groups.container = base.append('div') .style('position', 'relative'); groups.layer = groups.container.append('div') .style({ position: 'relative', width: '100%', height: '100%' }); groups.notifier = groups.container.append('svg') .style({ position: 'absolute', top: 0, left: 0 }) .attr({ width: '100%', height: '100%' }); } groups.layer.classed('layer-group', true); groups.notifier.classed('notifier', true) .style({ display: 'none', 'pointer-events': 'none' }); return groups; } module.exports = d3Chart('BaseWidget', { //********************************************************************************** // Anything in the initialization method will only run once. // i.e. It will not be run every time you call draw. //********************************************************************************** initialize: function () { // Make the chart constructor accessible to all other methods. var _Chart = this; //---------------------------------------------------------------------------------- // Default Values for the wiget's configurable options. // Valid types for UI elements are: string, number, color, select, boolean //---------------------------------------------------------------------------------- _Chart._config = { height: { name: 'Chart Height', description: '', category: 'Dimensions', type: 'number', value: 250, units: 'px' }, width: { name: 'Chart Width', description: '', category: 'Dimensions', type: 'number', value: 250, units: 'px' }, shouldValidate: { description: 'Flag for turning off data validation', type: 'boolean', value: true }, updateSizeableConfigs: { description: 'Flag for turning off the mimic of illustrator\'s scale functionality', type: 'boolean', value: true }, isOnMobile: { description: 'If true, it signals to the widget that it is running on a mobile device. Should be called before draw and then NEVER changed.', type:'boolean', value: false } }; //---------------------------------------------------------------------------------- // Data Definition: // Set functions on how to access and validate data values. //---------------------------------------------------------------------------------- // This should be set in the "extended chart". _Chart._dataDefinition = {}; //---------------------------------------------------------------------------------- // Static - Anything that is defined here will never change //---------------------------------------------------------------------------------- var isSvg = _Chart.base.node().namespaceURI.indexOf('svg') !== -1; var groups = createGroups(_Chart.base, isSvg); _Chart._layerGroup = groups.layer; //---------------------------------------------------------------------------------- // Notifier: // This must come after any other layers/groups //---------------------------------------------------------------------------------- _Chart._notifier = new Notifier(groups.notifier); _Chart._notifier .width(function () { return _Chart.config('width'); }); _Chart._notifier .height(function () { return _Chart.config('height'); }); _Chart._dim = function() { return Math.min(_Chart.c('width'), _Chart.c('height')); }; //---------------------------------------------------------------------------------- // Data validation function and chart variable setup. //---------------------------------------------------------------------------------- _Chart.transform = function (data) { _Chart.updateSizeableConfigs(); if (!isSvg) { groups.container.style({ width: _Chart.c('width') + 'px', height: _Chart.c('height') + 'px' }); } return _Chart.validateData(data); }; }, //---------------------------------------------------------------------------------- // Helper Functions //---------------------------------------------------------------------------------- /** * Merges the extending chart's config with the base's config * @param {object} newConfig The extending chart's config */ mergeConfig: function mergeConfig(newConfig) { var _Chart = this; if (_Chart._config) { _Chart._config = _.extend(_Chart._config, newConfig); } else { _Chart._config = newConfig; } }, /** * Merges the extending chart's data definitions with the base's data definition * @param {object} newDefinition The extending chart's data definition */ mergeDataDefinition: function mergeDataDefinition(newDefinition) { var _Chart = this; if (_Chart._dataDefinition) { _Chart._dataDefinition = _.extend(_Chart._dataDefinition, newDefinition); } else { _Chart._dataDefinition = newDefinition; } }, /** * Runs data though your validation functions and throws out bad data. * @param {Array} data The data you passed to the draw method of your chart. * @return {Array} The validated data. */ validateData: function (data) { var _Chart = this; var validData = data; _Chart._notifier.clearDataValidationMessages(); _Chart._notifier.base.style('display', 'none'); if (_Chart.c('shouldValidate')) { if (!validData || !_.flatten(validData).length) { validData = []; } else { validData = validData.filter(function (line) { for (var col in _Chart._dataDefinition) { if (!_Chart._dataDefinition[col].validate(line)) { _Chart._notifier.appendMessage(_Chart.c('chartName'), 'WARN', 'Invalid value at column for ' + col, _Chart._dataDefinition[col], line); return false; } } return true; }); } if (!validData.length) { _Chart._notifier.appendMessage(_Chart.config('chartName'), 'NO_DATA', 'No Data', _Chart._dataDefinition, data); _Chart._notifier.base.style('display', ''); } } _Chart._notifier.draw(); return validData; }, //---------------------------------------------------------------------------------- // Public Functions: // Getter and Setter functions are documented here. //---------------------------------------------------------------------------------- /** * Sets one or many of the data definition properties for the chart * @param {object} obj Contains data definiton objects you would like to set or update. * @return {object} Returns the chart for chainability. */ dataDefinition: function (obj) { if (arguments.length === 0) { return this._dataDefinition; } for (var key in obj) { if (this._dataDefinition[key]) { for (var subKey in obj[key]) { if (obj[key].hasOwnProperty(subKey)) { this._dataDefinition[key][subKey] = obj[key][subKey]; } } } else { console.warn(key + ' is not a valid data property.'); } } return this; }, /** * This will get or set any of the chart's configuration options defined * in the 'chart._config' object. * @param {String or Object} item If string, it will return the value for that config item. * * If object, it will update that config item with set value. * @param {String or Number} [value] The value to update config item with. * @return {object} The chart to preserve chainability. */ config: function (item, value) { var _Chart = this; if (item === undefined) { return this; } if (value === undefined && typeof item === 'string') { return this._config[item] ? this._config[item].value : undefined; } var items; if (typeof item === 'object') { items = item; } else { items = {}; items[item] = value; } for (var key in items) { var c = this._config[key]; if (!c) { console.warn(this.c('chartName') + ':' + key + ' is not a valid config property.'); } else { c.value = items[key]; if (isFinite(c.percent)) { var dim = c.dim || _Chart._dim; c.percent = c.value / dim(); } if (c.onChange) { console.warn(this.c('chartName') + ':' + key + ': Config\'s onChange function is deprecated and will be removed shortly.'); c.onChange.call(this, items[key]); } } } return this; }, /** * This will get or set any of the chart's accessors defined * in the 'chart._dataDefinition' object. * @param {String or Object} item If string, it will return the function for that accessor item. If object, it will update that accessor with set function. * @param {function} [value] The function to update accessor item with. * @return {object} The chart to preserve chainability. */ accessors: function (item, value) { var _Chart = this; if (item === undefined) { return this; } if (value === undefined && typeof item === 'string') { if (this._dataDefinition[item]) { return function(line) { return line ? _Chart._dataDefinition[item].accessor(line) : _Chart._dataDefinition[item].default; }; } else { console.warn(item + ' is not a valid accessor.'); return d3.scale.identity(); } } var items; if (typeof item === 'object') { items = item; } else { items = {}; items[item] = value; } for (var key in items) { if (this._dataDefinition[key]) { this._dataDefinition[key].accessor = items[key]; } else { console.warn(key + ' is not a valid accessor.'); } } return this; }, /** * Shorthand for config getter/setter */ c: function (item, value) { return this.config.apply(this, arguments); }, /** * Shorthand for accessor getter function */ a: function (item, value) { return this.accessors.apply(this, arguments); }, /** * Visits each config element. * If a config element has a size function it will update that configs value * with the returned result of its size function. */ updateSizeableConfigs: function () { if (!this.c('updateSizeableConfigs')) return; var _Chart = this; var configKeys = Object.keys(this._config); for (var i = 0; i < configKeys.length; i++) { var configElement = this._config[configKeys[i]]; if (isFinite(configElement.percent)) { var dim = configElement.dim || _Chart._dim; configElement.value = roundToOneDecimal(configElement.percent * dim()); } } }, /** * Takes a theme obj and applies it to the widget's config elements. * * @param themeObj {Object} {"name": "Dark Bold", "domoDefinedTheme": true, "config": { gaugeFillPrimaryColor: '#1C5CAE'} }; */ applyTheme: function (themeObj) { if (themeObj && themeObj.config) { var applyThemeConfigs = themeObj.config; var widgetConfigs = this._config; for (var widgetConfigElementName in widgetConfigs) { var hasValidThemeElement = (widgetConfigs.hasOwnProperty(widgetConfigElementName) && widgetConfigs[widgetConfigElementName].theme !== undefined); if (hasValidThemeElement) { if (applyThemeConfigs[widgetConfigs[widgetConfigElementName].theme]) { widgetConfigs[widgetConfigElementName].value = applyThemeConfigs[widgetConfigs[widgetConfigElementName].theme]; } } } return true; } return false; }, /** * This function will be called to clean up any pointers before the chart is destroyed. * Extending charts should implement this function. */ destroy: function destroy() {} }); /***/ }, /* 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__; /***/ } /******/ ]) }); ;