UNPKG

@domoinc/domo-select

Version:

DomoSelect - Domo Widget

453 lines (403 loc) 14.3 kB
var d3 = require('d3'); var d3Chart = require('d3.chart'); var Utilities = require('@domoinc/utilities'); var BaseWidget = require('@domoinc/base-widget'); require('style!css!./styles/dropdown.css'); //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- // Dropdown: // This is where you can give some high level documentation of your widget. //---------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- module.exports = BaseWidget.extend('Dropdown', { //********************************************************************************** // 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; _Chart.transform = function(data) { if (!data || data.length === 0) { _Chart.noResults = true; data = [['No Results']]; } else { _Chart.noResults = false; } _Chart.updateConfigs(); _Chart.validateInitialIndex(data); _Chart.dropdownContainer .style({ 'display': function() { if (_Chart.c('visible')) { _Chart.setScrollDimensions(); return 'block'; } else { return 'none'; } } }) .transition() .style({ 'opacity': function() { if (_Chart.c('visible')) { return 1; } else { return 0; } } }); _Chart.dropdownContainer .classed('domoScroll', _Chart.c('domoScroll')); _Chart.setScrollDimensions(); if (data.length >= (_Chart.c('selectedIndex') + 1)) { _Chart.c('selectedValue', _Chart.a('Value')(data[_Chart.c('selectedIndex')])) } else { _Chart.c('selectedValue', _Chart.a('Value')(data[0])) } return data; }; this._newConfig = { chartName: { description: 'Name of chart for Reporting.', type: 'string', value: 'Dropdown' }, visible: { description: 'Show/Hide the dropdown.', type: 'boolean', value: true }, selectedIndex: { description: 'Selected Index', type: 'number', value: 0 }, selectedValue: { description: 'selectedValue', type: 'string', value: 'ConfigValue' }, size: { description: 'Size of the dropdown.', type: 'string', value: 'medium', }, domoScroll: { description: 'Turn on domo Scroll?', type: 'boolean', value: false, }, width: { description: 'Width of the dropdown', type: 'string', value: '250px', }, height: { description: 'Max Height of the dropdown', type: 'string', value: '200px', }, listIconFunction: { description: 'Function that runs on every list element', type: 'function', value: function() {} }, listIconPadding: { description: 'Padding for the list icons', type: 'number', value: 0 } }; // Update config object from base chart. this.mergeConfig(_Chart._newConfig); _Chart._newDataDefinition = { 'Label': { type: 'string', validate: function (d) { return this.accessor(d) !== "undefined";}, accessor: function (line) { return String(line[0]); }, }, 'Value': { type: 'string', validate: function (d) { return this.accessor(d) !== "undefined";}, accessor: function (line) { return String(line[1]); }, } }; // Update data defs object from base chart. this.mergeDataDefinition(_Chart._newDataDefinition); //---------------------------------------------------------------------------------- // Interface - This is where you document your events. The chart's .on()' and // '.trigger()' methods will go here. //---------------------------------------------------------------------------------- _Chart.on('visibility', _Chart.toggleVisibility) //---------------------------------------------------------------------------------- // Static - Anything that is defined here will never change //---------------------------------------------------------------------------------- _Chart.dropdownContainer = _Chart._layerGroup .classed('da-dropdown', true); _Chart._dropdownGroup = _Chart.dropdownContainer.append("ul") .classed('dropdownList', true); _Chart.scroll = d3.scroll(); //---------------------------------------------------------------------------------- // Layers: This is where you'll place your layers. //---------------------------------------------------------------------------------- //********************************************************************************** //Place a small description of the layer here. //********************************************************************************** _Chart.layer('layer', this.dropdownContainer, { //********************************************************************************** // The 'this' context is the selection passed into layer. // You must return a d3 selection bound with data. //********************************************************************************** dataBind: function(data) { return _Chart._dropdownGroup.selectAll('li').data(data); }, //********************************************************************************** // The 'this' context is the 'enter()' selection. // Appends all elements w/o any style, attr, or on function calls. //********************************************************************************** insert: function() { var listElements = this.append('li') .classed('dropdownItem', true); if (!_Chart.noResults) { listElements.append('div') .classed('listItemIcon', true); } listElements.append('div') .classed('dropdownItemContainer', true) .style('pointer-events', 'none'); return listElements; }, //---------------------------------------------------------------------------------- // Element Life Cycle Events //---------------------------------------------------------------------------------- events: { //********************************************************************************** // The 'this' context is also the 'enter()' selection. // You can use this to append styles and attributes to inserted elements. //********************************************************************************** 'enter': function() { this.on('click', function(d, i) { _Chart.c('selectedIndex', i); _Chart.c('selectedValue', _Chart.a('Value')(d)); // Trigger click and pass data, allowing use to handle selecting element how they want _Chart.trigger('click', d, i); _Chart._dropdownGroup.selectAll('.active').classed('active', false); d3.select(this).classed('active', true); }); return this; }, //********************************************************************************** // This is the selection returned from the dataBind method. It's the selection // that contains all (existing and newly bound) elements. //********************************************************************************** 'merge': function() { this.classed('active', function(d, i) { if (_Chart.noResults) { return false; } else { return i === Number(_Chart.c('selectedIndex')) ? true : false; } }) this.classed('disabled', _Chart.noResults) this.select("div.dropdownItemContainer") .text(function(d) { return _Chart.a('Label')(d); }); if (_Chart.c('domoScroll')) { _Chart.scroll.resize(); } if (!_Chart.noResults) { this.select('.listItemIcon').call(function() { this.each(function(d, i) { var s = d3.select(this); _Chart.c('listIconFunction')(s, d, i); _Chart.reCalcIconPadding(s, _Chart.c('listIconPadding')) }) }) } return this; }, //********************************************************************************** // The 'this' context is the exit() selection. //********************************************************************************** 'exit': function() { if (_Chart.c('domoScroll')) { _Chart.scroll({ x: 0, y: 0 }); } return this.remove(); } }, }); _Chart.updateConfigs = function() { //size switch(_Chart.c('size')) { case 'small': _Chart.dropdownContainer.classed('dd-sm', true); _Chart.dropdownContainer.classed('dd-md', false); _Chart.dropdownContainer.classed('dd-lg', false); break; case 'large': _Chart.dropdownContainer.classed('dd-sm', false); _Chart.dropdownContainer.classed('dd-md', false); _Chart.dropdownContainer.classed('dd-lg', true); break; default: _Chart.dropdownContainer.classed('dd-sm', false); _Chart.dropdownContainer.classed('dd-md', true); _Chart.dropdownContainer.classed('dd-lg', false); } //domoScroll if (!_Chart.c('domoScroll')) { _Chart.dropdownContainer .classed('domoScroll', false); _Chart.scroll({ x: 0, y: 0 }); _Chart._dropdownGroup.on('wheel', function() { return }); _Chart._dropdownGroup.on('mousewheel', function() { return }); } //width var width = parseInt(_Chart.c('width')) - 8; width = width < 0 ? 0 : width; _Chart.dropdownContainer.style({ 'width': width + 'px', 'margin-left': '3px' }); //height var height = _Chart.c('height'); if (typeof height === 'number') { height = height + 'px'; } _Chart.dropdownContainer.style({ 'max-height': height }) } /** * ensure the initial index is within the range of the data length */ _Chart.validateInitialIndex = function(data) { if (_Chart.c('selectedIndex') < 0) { _Chart.c('selectedIndex', 0); return; } else if (_Chart.c('selectedIndex') > data.length - 1) { _Chart.c('selectedIndex', data.length - 1); return; } return; } _Chart.toggleVisibility = function() { _Chart.c('visible', !_Chart.c('visible')); _Chart.dropdownContainer .style({ 'display': function() { if (_Chart.c('visible')) { _Chart.setScrollDimensions(); return 'block'; } else { return 'none'; } } }) .transition() .style({ 'opacity': function() { if (_Chart.c('visible')) { return 1; } else { return 0; } } }); }; _Chart.reCalcIconPadding = function(iconDiv, padding) { var bbox = iconDiv.node().getBoundingClientRect(); d3.select(iconDiv.node().parentNode).select('.dropdownItemContainer') .style({ 'margin-left': (bbox.width + padding) + 'px' }); } _Chart.setSelectedValueByLabel = function (label) { var item = _Chart._dropdownGroup .selectAll('li.dropdownItem').filter(function (d) { return _Chart.a('Label')(d) === label; }); if (item && item.node()) { item.node().click(); } }; _Chart.setSelectedValueByIndex = function (index) { var item = _Chart._dropdownGroup .selectAll('li.dropdownItem').filter(function (d, i) { return i === index; }); if (item && item.node()) { item.node().click(); } }; _Chart.setScrollDimensions = function () { if (_Chart.c('domoScroll')) { _Chart.scroll .viewWidth(parseInt(_Chart.c('width'))) .viewHeight(parseInt(_Chart.c('height'))) .syncY(_Chart._dropdownGroup); } }; }, //************************************************** //Legacy getters/setters for backwards compatibility //************************************************** width: function(_) { if (arguments.length === 0) { return this.c('width'); } this.c('width', _); return this; }, height: function(_) { if (arguments.length === 0) { return this.c('height'); } this.c('height', _); return this; }, size: function(_) { if (arguments.length === 0) { return this.c('size'); } this.c('size', _); return this }, chartName: function(_) { if (arguments.length === 0) { return this.c('chartName') } this.c('chartName', _); return this; }, selectedIndex: function(_) { if (arguments.length === 0) { return this.c('selectedIndex'); } this.c('selectedIndex', _); return this; }, selectedValue: function(_) { if (arguments.length === 0){ return this.c('selectedValue'); } this.c('selectedValue', _); return this; }, showDropdown: function(_) { if (arguments.length === 0){ return this.c('visible'); } this.c('visible', _); return this; }, });