@domoinc/legend-connector
Version:
LegendConnector - Domo Widget
295 lines (268 loc) • 9.2 kB
JavaScript
var defaultWidthRatio = 0.375;
var defaultHeightRatio = 0.075;
/**
* depending on when you merge the legend configs, you can change its order in the ADS
*/
function getLegendConfigs(daTheme2) {
return {
showLegend: {
name: 'Show Legend',
description: 'Show or hide the legend',
type: 'select',
value: {name: 'Hide', value: false},
options: [
{name: 'Show', value: true},
{name: 'Hide', value: false},
]
},
numberOfLegendItems: {
name: 'Number Of Legend Items',
description: 'Maximum number of items to display in the legend',
type: 'number',
value: 6
},
legendWidth: {
name: 'Legend Width',
description: 'Width of the legend',
value: 150,
type: 'number',
units: 'px'
},
legendRowHeight: {
name: 'Legend Row Height',
description: 'Height of each row in the legend',
value: 30,
type: 'number',
units: 'px'
},
legendFontFamily: daTheme2.themeElements.legendFontFamily(),
legendFontColor: daTheme2.themeElements.legendFontColor(),
legendPosition: {
description: 'Position object to position the legend properly when pulling from the artboard'
}
};
}
/**
* add or remove legend group and widget
* depending on if show legend config is set to true or false
*/
function initializeLegend(_Chart, Legend, colorScale) {
//remove legend if config is set to hide
if (!_Chart.c('showLegend').value) {
_Chart._layerGroup.select('.legend').remove();
_Chart._legendGroup = undefined;
return;
}
//if the legend group is not there yet, add it
if (_Chart._legendGroup === undefined) {
_Chart._legendGroup = _Chart._layerGroup.append('g')
.classed('legend', true)
.attr({
id: 'Legend',
'transform': 'translate(' + _Chart.c('legendPosition').left + ', ' + _Chart.c('legendPosition').top + ')'
});
_Chart._legend = new Legend(_Chart._legendGroup);
}
updateLegend(_Chart, colorScale);
}
/**
* update legend configs
*/
function updateLegend(_Chart, colorScale) {
//set configs for legend
_Chart._legend.c({
'colorScale': colorScale,
'legendWidth': _Chart.c('legendWidth'),
'rowWidth': _Chart.c('legendWidth'),
'rowHeight': _Chart.c('legendRowHeight'),
'numberOfItemsToShow': _Chart.c('numberOfLegendItems'),
'fontFamily': _Chart.c('legendFontFamily'),
'textColor': _Chart.c('legendFontColor'),
'icons': function(container) {
container.each(function(d, i) {
d3.select(this)
.append('rect')
.attr({
x: 0,
y: 0,
height: _Chart.c('legendRowHeight')/2,
width: _Chart.c('legendRowHeight')/2
});
});
return container;
}
})
}
/**
* get the series from the data and
* pass that data into draw legend
*/
function drawLegend(_Chart, data, seriesAccessor) {
if (_Chart.c('showLegend').value) {
var legendData = [];
var set = d3.set();
data.forEach(function(line) {
set.add(seriesAccessor(line));
});
var values = set.values();
for (var i = 0; i < values.length; i++) {
legendData.push([values[i]]);
}
_Chart._legend.draw(legendData);
}
}
/**
* get an object for legend position comparable to getBoundingClientRect
* if there is no legend chartbounds, then create a default legend group
*
* the legend width and height in the config page are set by the legend width and height configs
* the legend width and height when pulling from artboard are set by the chartbounds of the legend on the artboard
*/
function getLegendPosition(container) {
if (container.selectAll('[id^=legendChartBounds]').node()) {
var widgetClient = container.select('[id^=chartBounds]')
.node()
.getBoundingClientRect();
var legendClient = container.selectAll('[id^=legendChartBounds]')
.node()
.getBoundingClientRect();
//make an object copy of the clientRect so it is mutable
var legendClientClone = {};
for (var key in legendClient) {
legendClientClone[key] = legendClient[key];
}
// The legend position must be countertranslated by the left or top of the widgets chartbounds to be placed correctly
// If this is removed, adding the legend to the left or top of the widget and pulling it from artboard will place the legend at left = 0 or top = 0
legendClientClone.top -= widgetClient.top;
legendClientClone.left -= widgetClient.left;
return legendClientClone;
}
return;
}
/**
* legendPosition is undefined if no legendChartBounds existed
* create a default legend position object if undefinied
*/
function setLegendPosition(widget, legendPosition) {
if (legendPosition) {
widget.c('legendPosition', legendPosition);
} else {
var legendHeight = widget.c('legendRowHeight') * widget.c('numberOfLegendItems');
var defaultLegendPosition = {
width: 0,
height: 0,
left: widget.c('width') + widget.c('width')*0.2, //widget.c('width')*0.2 is padding between the widget and legend
top: widget.c('height')/2 - legendHeight/2
};
widget.c({
legendPosition: defaultLegendPosition,
legendWidth: widget.c('width')*defaultWidthRatio,
legendRowHeight: widget.c('height')*defaultHeightRatio, //default the new legend width and height to these ratios
})
}
}
/**
* add chartbounds for the widget
* if add legend config is set to true, add legendchartbounds as well
*/
function prepareForArtboard(widget, seriesAccessor) {
return function() {
if (widget.base) {
widget.base.selectAll('[id^=chartBounds]').remove();
widget.base.insert('rect', ':first-child')
.attr({
id: 'chartBounds',
height: widget.c('height'),
width: widget.c('width'),
})
.style('fill', 'none');
if (widget.c('showLegend').value) {
var numRows = getNumRows(widget, seriesAccessor);
var legendWidth = widget.c('legendWidth');
var legendHeight = numRows * widget.c('legendRowHeight');
widget.base.selectAll('[id^=legendChartBounds]').remove();
widget.base.select('[id^=Legend]')
.insert('rect', ':first-child')
.attr({
id: 'legendChartBounds',
width: legendWidth,
height: legendHeight,
})
.style('fill', 'none');
}
}
}
}
/**
* check to see if widget is being pulled from artboard
* if it is, update the legend configs via the configs stored in AutoWidgets
* updating via widgets.c() will not work in ADS if value is changed on artboard
*/
function pullFromArtboard(AutoWidgets, widget, seriesAccessor) {
for (var key in AutoWidgets.configs) {
if (AutoWidgets.configs[key].config && AutoWidgets.configs[key].config.widgetName.value === widget.dataName()) {
var config = AutoWidgets.configs[key].config;
//update number of legend items to get the right number of rows
widget.c('numberOfLegendItems', config.numberOfLegendItems.value);
var numRows = getNumRows(widget, seriesAccessor);
//update legend dimensions
//if legend dimensions are 0 or legend chartbounds are deleted, will change value to default legend dimensions
var legendPosition = widget.c('legendPosition');
config.legendWidth.value = legendPosition.width ? legendPosition.width : widget.c('width')*defaultWidthRatio;
config.legendRowHeight.value = legendPosition.height ? legendPosition.height / numRows : widget.c('height')*defaultHeightRatio;
}
}
}
/**
* calculate the number of rows to display
* it will be the lesser of the number of series
* or the legend config, numberOfLegendItems
*/
function getNumRows(widget, seriesAccessor) {
//get unique series
var dataSchema = widget._dataSchema;
var set = d3.set();
for (var key in dataSchema) {
dataSchema[key].data.forEach(function(line) {
set.add(seriesAccessor(line));
});
}
var seriesLength = set.values().length;
//calculate number of rows to show
var maxNumLegendRows = widget.c('numberOfLegendItems');
var numRows = d3.min([seriesLength, maxNumLegendRows]);
return numRows;
}
// screenshot the legend seperate from the rest of the widget
function rasterizeNodes(widget) {
return function() {
var nodes = [];
var children = widget._layerGroup.node().childNodes;
for (var i = 0; i < children.length; i++) {
var node = children[i];
if (node.classList && node.classList.contains('legend')) {
nodes.push({
node: node.querySelector('.layer-group')
});
}
else {
nodes.push({
node: node
});
}
}
return nodes;
};
}
module.exports = {
//Widget.js functions
getLegendConfigs: getLegendConfigs,
initializeLegend: initializeLegend,
drawLegend: drawLegend,
//DomoWidget.js functions
getLegendPosition: getLegendPosition,
setLegendPosition: setLegendPosition,
prepareForArtboard: prepareForArtboard,
pullFromArtboard: pullFromArtboard,
rasterizeNodes: rasterizeNodes
}