blessed-contrib
Version:
Build dashboards (or any other application) using ascii/ansi art and javascript.
218 lines (187 loc) • 5.75 kB
JavaScript
'use strict';
var blessed = require('blessed')
, Node = blessed.Node
, Canvas = require('../canvas')
, utils = require('../../utils.js');
function StackedBar(options) {
if (!(this instanceof Node)) {
return new StackedBar(options);
}
var self = this;
Canvas.call(this, options, require('ansi-term'));
this.options.barWidth = this.options.barWidth || 6;
this.options.barSpacing = this.options.barSpacing || 9;
if ((this.options.barSpacing - this.options.barWidth) < 3) {
this.options.barSpacing = this.options.barWidth + 3;
}
this.options.xOffset = this.options.xOffset==null? 5 : this.options.xOffset;
if (this.options.showText === false)
this.options.showText = false;
else
this.options.showText = true;
this.options.legend = this.options.legend || {};
if (this.options.showLegend === false)
this.options.showLegend = false;
else
this.options.showLegend = true;
this.on('attach', function() {
if (self.options.data) {
self.setData(self.options.data);
}
});
}
StackedBar.prototype = Object.create(Canvas.prototype);
StackedBar.prototype.calcSize = function() {
this.canvasSize = {width: this.width-2, height: this.height};
};
StackedBar.prototype.getSummedBars = function(bars) {
var res = [];
bars.forEach(function(stackedValues) {
var sum = stackedValues.reduce(function(a,b) {
return a + b;
} , 0);
res.push(sum);
});
return res;
};
StackedBar.prototype.setData = function(bars) {
if (!this.ctx) {
throw 'error: canvas context does not exist. setData() for bar charts must be called after the chart has been added to the screen via screen.append()';
}
this.clear();
var summedBars = this.getSummedBars(bars.data);
var maxBarValue = Math.max.apply(Math, summedBars);
if (this.options.maxValue)
maxBarValue = Math.max(maxBarValue, this.options.maxValue);
var x = this.options.xOffset;
for (var i = 0; i < bars.data.length; i++) {
this.renderBar(x, bars.data[i], summedBars[i], maxBarValue, bars.barCategory[i]);
x += this.options.barSpacing;
}
this.addLegend(bars, x);
};
StackedBar.prototype.renderBar = function(x, bar, curBarSummedValue, maxBarValue, category) {
/*
var c = this.ctx
c.strokeStyle = 'red';
c.fillRect(0,7,4,0)
c.strokeStyle = 'blue';
c.fillRect(0,4,4,1)
c.strokeStyle = 'green';
c.fillRect(5,7,4,2)
return
*/
//first line is for label
const BUFFER_FROM_TOP = 2;
const BUFFER_FROM_BOTTOM = (this.options.border ? 2 : 0) + (this.options.showText ? 1 : 0);
var c = this.ctx;
c.strokeStyle = 'normal';
c.fillStyle = 'white';
if (this.options.labelColor)
c.fillStyle = this.options.labelColor;
if (this.options.showText) {
c.fillText(category, x + 1, this.canvasSize.height - BUFFER_FROM_BOTTOM);
}
if (curBarSummedValue < 0) return;
var maxBarHeight = this.canvasSize.height - BUFFER_FROM_TOP - BUFFER_FROM_BOTTOM;
var currentBarHeight = Math.round(maxBarHeight * (curBarSummedValue / maxBarValue));
//start painting from bottom of bar, section by section
var y = maxBarHeight + BUFFER_FROM_TOP;
var availableBarHeight = currentBarHeight;
for (var i=0; i < bar.length; i++) {
var currStackHeight = this.renderBarSection(
x,
y,
bar[i],
curBarSummedValue,
currentBarHeight,
availableBarHeight,
this.options.barBgColor[i]);
y -= currStackHeight;
availableBarHeight -= currStackHeight;
}
};
StackedBar.prototype.renderBarSection = function(
x,
y,
data,
curBarSummedValue,
currentBarHeight,
availableBarHeight,
bg) {
var c = this.ctx;
var currStackHeight = currentBarHeight <= 0?
0 :
Math.min(
availableBarHeight, //round() can make total stacks excceed curr bar height so we limit it
Math.round(currentBarHeight * (data / curBarSummedValue))
);
c.strokeStyle = bg;
if (currStackHeight>0) {
var calcY = y - currStackHeight;
/*fillRect starts from the point bottom of start point so we compensate*/
var calcHeight = Math.max(0, currStackHeight-1);
c.fillRect(
x,
calcY,
this.options.barWidth,
calcHeight
);
c.fillStyle = 'white';
if (this.options.barFgColor)
c.fillStyle = this.options.barFgColor;
if (this.options.showText) {
var str = utils.abbreviateNumber(data.toString());
c.fillText(
str,
Math.floor(x + this.options.barWidth/2 + str.length/2),
calcY + Math.round(calcHeight/2));
}
}
return currStackHeight;
};
StackedBar.prototype.getOptionsPrototype = function() {
return { barWidth: 1
, barSpacing: 1
, xOffset: 1
, maxValue: 1
, barBgColor: 's'
, data: { barCategory: ['s']
, stackedCategory: ['s']
, data: [ [ 1] ]
}
};
};
StackedBar.prototype.addLegend = function(bars, x) {
var self = this;
if (!self.options.showLegend) return;
if (self.legend) self.remove(self.legend);
var legendWidth = self.options.legend.width || 15;
self.legend = blessed.box({
height: bars.stackedCategory.length+2,
top: 1,
width: legendWidth,
left: x,
content: '',
fg: 'green',
tags: true,
border: {
type: 'line',
fg: 'black'
},
style: {
fg: 'blue',
},
screen: self.screen
});
var legandText = '';
var maxChars = legendWidth-2;
for (var i=0; i<bars.stackedCategory.length; i++) {
var color = utils.getColorCode(self.options.barBgColor[i]);
legandText += '{'+color+'-fg}'+ bars.stackedCategory[i].substring(0, maxChars)+'{/'+color+'-fg}\r\n';
}
self.legend.setContent(legandText);
self.append(self.legend);
};
StackedBar.prototype.type = 'bar';
module.exports = StackedBar;