messengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscord
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
358 lines
12 KiB
9 years ago
|
/**
|
||
|
* @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);
|
||
|
});
|