Форк Rambox
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
12 KiB

/**
* @class Ext.sparkline.Bar
*
* Plots a bar chart of the values in the passed {@link #values} array.
*
* See {@link Ext.sparkline.Base the base class} for a simple example.
*/
Ext.define('Ext.sparkline.Bar', {
extend: 'Ext.sparkline.BarBase',
requires: [
'Ext.sparkline.RangeMap'
],
alias: 'widget.sparklinebar',
config: {
/**
* @cfg {String} [barColor=#3366cc] The bar color for positive values.
*/
barColor: '#3366cc',
/**
* @cfg {String} [negBarColor=#f44] The bar color for negative values.
*/
negBarColor: '#f44',
/**
* @cfg {String[]} [stackedBarColor] An array of colours to use for stacked bar charts.
* The first series will use the first value in the array, the second series will use the second, etc.
*/
stackedBarColor: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00', '#dd4477', '#0099c6', '#990099'],
/**
* @cfg {String} [zeroColor] The bar color for zero values.
*/
zeroColor: null,
/**
* @cfg {String} [nullColor] The bar color for null values. Usually null values are omitted and not plotted. Setting
* this config causes a very thin bar to be plotted with the special color in the case that null is a meaningful value in the series.
*/
nullColor: null,
/**
* @cfg {Boolean} [zeroAxis=true] Centers the Y axis at zero by default.
*/
zeroAxis: true,
/**
* @cfg {Number} [barWidth=4] The pixel width of bars.
*/
barWidth: 4,
/**
* @cfg {Number} [barSpacing=1] The pixel spacing between bars.
*/
barSpacing: 1,
/**
* @cfg {Number} [chartRangeMin] The minimum value to use for the range of Y values of the chart - Defaults to the minimum value supplied.
*/
chartRangeMin: null,
/**
* @cfg {Number} [chartRangeMax] The maximum value to use for the range of Y values of the chart - Defaults to the minimum value supplied.
*/
chartRangeMax: null,
/**
* @cfg {Boolean} chartRangeClip If true then the y values supplied to plot will be clipped to fall
* between {@link #chartRangeMin} and {@link #chartRangeMax} - By default chartRangeMin/Max just ensure that the chart
* spans at least that range of values, but does not constrain it.
*/
chartRangeClip: false,
/**
* @inheritdoc Ext.sparkline.TriState
*/
colorMap: null,
tipTpl: new Ext.XTemplate('● {prefix}{value}{suffix}')
},
remove: function (vals, filter) {
var i, vl, result = [];
for (i = 0, vl = vals.length; i < vl; i++) {
if (vals[i] !== filter) {
result.push(vals[i]);
}
}
return result;
},
// determine if all values of an array match a value
// returns true if the array is empty
all: function(arr, val, ignoreNull) {
var i;
for (i = arr.length; i--; ) {
if (ignoreNull && arr[i] === null) {
continue;
}
if (arr[i] !== val) {
return false;
}
}
return true;
},
applyColorMap: function(colorMap) {
var me = this;
if (Ext.isArray(colorMap)) {
me.colorMapByIndex = colorMap;
me.colorMapByValue = null;
} else {
me.colorMapByIndex = null;
me.colorMapByValue = colorMap;
if (me.colorMapByValue && me.colorMapByValue.get == null) {
me.colorMapByValue = new Ext.sparkline.RangeMap(colorMap);
}
}
return colorMap;
},
onUpdate: function () {
var me = this,
values = me.values,
barWidth = me.getBarWidth(),
barSpacing = me.getBarSpacing(),
chartRangeMin = me.getChartRangeMin(),
chartRangeMax = me.getChartRangeMax(),
chartRangeClip = me.getChartRangeClip(),
stackMin = Infinity,
stackMax = -Infinity,
isStackString, groupMin, groupMax, stackRanges,
numValues, i, vlen, range, zeroAxis = me.getZeroAxis(), xAxisOffset, min, max, clipMin, clipMax,
stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc,
stackTotals = [],
stackRangesNeg = [];
// scan values to determine whether to stack bars
for (i = 0, vlen = values.length; i < vlen; i++) {
val = values[i];
isStackString = typeof(val) === 'string' && val.indexOf(':') > -1;
if (isStackString || Ext.isArray(val)) {
stacked = true;
if (isStackString) {
val = values[i] = me.normalizeValues(val.split(':'));
}
val = me.remove(val, null); // min/max will treat null as zero
groupMin = Math.min.apply(Math, val);
groupMax = Math.max.apply(Math, val);
if (groupMin < stackMin) {
stackMin = groupMin;
}
if (groupMax > stackMax) {
stackMax = groupMax;
}
}
}
me.stacked = stacked;
me.regionShapes = {};
me.totalBarWidth = barWidth + barSpacing;
me.width = (values.length * barWidth) + ((values.length - 1) * barSpacing);
if (chartRangeClip) {
clipMin = chartRangeMin == null ? -Infinity : chartRangeMin;
clipMax = chartRangeMax == null ? Infinity : chartRangeMax;
}
numValues = [];
stackRanges = stacked ? [] : numValues;
for (i = 0, vlen = values.length; i < vlen; i++) {
if (stacked) {
vlist = values[i];
values[i] = svals = [];
stackTotals[i] = 0;
stackRanges[i] = stackRangesNeg[i] = 0;
for (j = 0, slen = vlist.length; j < slen; j++) {
val = svals[j] = chartRangeClip ? Ext.Number.constrain(vlist[j], clipMin, clipMax) : vlist[j];
if (val !== null) {
if (val > 0) {
stackTotals[i] += val;
}
if (stackMin < 0 && stackMax > 0) {
if (val < 0) {
stackRangesNeg[i] += Math.abs(val);
} else {
stackRanges[i] += val;
}
} else {
stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin));
}
numValues.push(val);
}
}
} else {
val = chartRangeClip ? Ext.Number.constrain(values[i], clipMin, clipMax) : values[i];
val = values[i] = me.normalizeValue(val);
if (val !== null) {
numValues.push(val);
}
}
}
me.max = max = Math.max.apply(Math, numValues);
me.min = min = Math.min.apply(Math, numValues);
me.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max;
me.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min;
if (chartRangeMin != null && (chartRangeClip || chartRangeMin < min)) {
min = chartRangeMin;
}
if (chartRangeMax != null && (chartRangeClip || chartRangeMax > max)) {
max = chartRangeMax;
}
if (min <= 0 && max >= 0 && zeroAxis) {
xAxisOffset = 0;
} else if (!zeroAxis) {
xAxisOffset = min;
} else if (min > 0) {
xAxisOffset = min;
} else {
xAxisOffset = max;
}
me.xAxisOffset = xAxisOffset;
range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - min;
// as we plot zero/min values a single pixel line, we add a pixel to all other
// values - Reduce the effective canvas size to suit
me.canvasHeightEf = (zeroAxis && min < 0) ? me.getHeight() - 2 : me.getHeight() - 1;
if (min < xAxisOffset) {
yMaxCalc = (stacked && max >= 0) ? stackMax : max;
yoffset = (yMaxCalc - xAxisOffset) / range * me.getHeight();
if (yoffset !== Math.ceil(yoffset)) {
me.canvasHeightEf -= 2;
yoffset = Math.ceil(yoffset);
}
} else {
yoffset = me.getHeight();
}
me.yoffset = yoffset;
me.range = range;
},
getRegion: function (x, y) {
var result = Math.floor(x / this.totalBarWidth);
return (result < 0 || result >= this.values.length) ? undefined : result;
},
getRegionFields: function (region) {
var values = Ext.Array.from(this.values[region]),
result = [],
value, i;
for (i = values.length; i--;) {
value = values[i];
result.push({
isNull: value === null,
value: value,
color: this.calcColor(i, value, region),
offset: region
});
}
return result;
},
calcColor: function (stacknum, value, valuenum) {
var me = this,
colorMapByIndex = me.colorMapByIndex,
colorMapByValue = me.colorMapByValue,
color, newColor,
zeroColor = me.getZeroColor();
if (this.stacked) {
color = me.getStackedBarColor();
} else {
color = (value < 0) ? me.getNegBarColor() : me.getBarColor();
}
if (value === 0 && zeroColor !== undefined) {
color = zeroColor;
}
if (colorMapByValue && (newColor = colorMapByValue.get(value))) {
color = newColor;
} else if (colorMapByIndex && colorMapByIndex.length > valuenum) {
color = colorMapByIndex[valuenum];
}
return Ext.isArray(color) ? color[stacknum % color.length] : color;
},
/*
* Render bar(s) for a region
*/
renderRegion: function(valuenum, highlight) {
var me = this,
vals = me.values[valuenum],
xaxisOffset = me.xAxisOffset,
range = me.range,
stacked = me.stacked,
canvas = me.canvas,
barWidth = me.getBarWidth(),
x = valuenum * me.totalBarWidth,
canvasHeightEf = me.canvasHeightEf,
yoffset = me.yoffset,
y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin,
nullColor = me.getNullColor();
vals = Ext.isArray(vals) ? vals : [vals];
valcount = vals.length;
val = vals[0];
isNull = me.all(vals, null);
allMin = me.all(vals, xaxisOffset, true);
if (isNull) {
if (nullColor) {
color = highlight ? nullColor : me.calcHighlightColor(nullColor, me);
y = (yoffset > 0) ? yoffset - 1 : yoffset;
canvas.drawRect(x, y, barWidth - 1, 0, color, color).append();
}
return;
}
yoffsetNeg = yoffset;
for (i = 0; i < valcount; i++) {
val = vals[i];
if (stacked && val === xaxisOffset) {
if (!allMin || minPlotted) {
continue;
}
minPlotted = true;
}
if (range > 0) {
height = Math.floor(canvasHeightEf * ((Math.abs(val - xaxisOffset) / range))) + 1;
} else {
height = 1;
}
if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) {
y = yoffsetNeg;
yoffsetNeg += height;
} else {
y = yoffset - height;
yoffset -= height;
}
color = me.calcColor(i, val, valuenum);
if (highlight) {
color = me.calcHighlightColor(color, me);
}
canvas.drawRect(x, y, barWidth - 1, height - 1, color, color).append();
}
}
}, function(cls) {
cls.onClassCreated(cls);
});