@domoinc/domo-select
Version:
DomoSelect - Domo Widget
453 lines (403 loc) • 14.3 kB
JavaScript
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;
},
});