telegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchat
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.
550 lines
16 KiB
550 lines
16 KiB
/** |
|
* @class Ext.sparkline.Base |
|
* |
|
* The base class for ExtJS SparkLines. SparkLines are small, inline graphs used to visually |
|
* display small amounts of data. For large datasets, use the {@link Ext.chart.AbstractChart chart package}. |
|
* |
|
* The SparkLine subclasses accept an {@link #values array of values}, and present the data in different visualizations. |
|
* |
|
* @example |
|
* new Ext.Panel({ |
|
* height: 300, |
|
* width: 600, |
|
* frame: true, |
|
* title: 'Test Sparklines', |
|
* renderTo:document.body, |
|
* bodyPadding: 10, |
|
* |
|
* // Named listeners will resolve to methods in this Panel |
|
* defaultListenerScope: true, |
|
* |
|
* // Named references will be collected, and can be access from this Panel |
|
* referenceHolder: true, |
|
* |
|
* items: [{ |
|
* reference: 'values', |
|
* xtype: 'textfield', |
|
* fieldLabel: 'Values', |
|
* validator: function(v) { |
|
* var result = []; |
|
* |
|
* v = v.replace(/\s/g, ''); |
|
* v = v.replace(/,$/, ''); |
|
* v = v.split(','); |
|
* for (var i = 0; i < v.length; i++) { |
|
* if (!Ext.isNumeric(v[i])) { |
|
* return 'Value must be a comma separated array of numbers'; |
|
* } |
|
* result.push(parseInt(v[i], 10)); |
|
* } |
|
* this.values = result; |
|
* return true; |
|
* }, |
|
* listeners: { |
|
* change: 'onTypeChange', |
|
* buffer: 500, |
|
* afterrender: { |
|
* fn: 'afterTypeRender', |
|
* single: true |
|
* } |
|
* } |
|
* }, { |
|
* reference: 'type', |
|
* xtype: 'combobox', |
|
* fieldLabel: 'Type', |
|
* store: [ |
|
* ['sparklineline', 'Line'], |
|
* ['sparklinebox', 'Box'], |
|
* ['sparklinebullet', 'Bullet'], |
|
* ['sparklinediscrete', 'Discrete'], |
|
* ['sparklinepie', 'Pie'], |
|
* ['sparklinetristate', 'TriState'] |
|
* ], |
|
* value: 'sparklineline', |
|
* listeners: { |
|
* change: 'onTypeChange', |
|
* buffer: 500 |
|
* } |
|
* }], |
|
* |
|
* // Start with a line plot. |
|
* afterTypeRender: function(typeField) { |
|
* typeField.setValue('6,10,4,-3,7,2'); |
|
* }, |
|
* |
|
* onTypeChange: function() { |
|
* var me = this, |
|
* refs = me.getReferences(), |
|
* config; |
|
* |
|
* if (me.sparkLine) { |
|
* me.remove(me.sparkLine, true); |
|
* } |
|
* config = { |
|
* xtype: refs.type.getValue(), |
|
* values: refs.values.values, |
|
* height: 25, |
|
* width: 100 |
|
* }; |
|
* me.sparkLine = Ext.create(config); |
|
* me.add(me.sparkLine); |
|
* |
|
* // Put under fields |
|
* me.sparkLine.el.dom.style.marginLeft = refs.type.labelEl.getWidth() + 'px'; |
|
* } |
|
* }); |
|
* |
|
*/ |
|
Ext.define('Ext.sparkline.Base', { |
|
extend: 'Ext.Widget', |
|
requires: [ |
|
'Ext.XTemplate', |
|
'Ext.sparkline.CanvasCanvas', |
|
'Ext.sparkline.VmlCanvas' |
|
], |
|
|
|
cachedConfig: { |
|
baseCls: Ext.baseCSSPrefix + 'sparkline', |
|
|
|
/** |
|
* @cfg {String} [lineColor=#157fcc] The hex value for line colors in graphs which display lines ({@link Ext.sparkline.Box Box}, {@link Ext.sparkline.Discrete Discrete and {@link Ext.sparkline.Line Line}). |
|
*/ |
|
lineColor: '#157fcc', |
|
|
|
/** |
|
* @cfg {String} [fillColor=#def] The hex value for fill color in graphs which fill areas ({@link Ext.sparkline.Line Line}). |
|
*/ |
|
fillColor: '#def', |
|
|
|
defaultPixelsPerValue: 3, |
|
|
|
tagValuesAttribute: 'values', |
|
|
|
enableTagOptions: false, |
|
|
|
enableHighlight: true, |
|
|
|
/** |
|
* @cfg {String} [highlightColor=null] The hex value for the highlight color to use when mouseing over a graph segment. |
|
*/ |
|
highlightColor: null, |
|
|
|
/** |
|
* @cfg {Number} [highlightLighten=1.4] How much to lighten the highlight color by when mouseing over a graph segment. |
|
*/ |
|
highlightLighten: 1.4, |
|
|
|
/** |
|
* @cfg {Boolean} [tooltipSkipNull=true] Null values will not have a tooltip displayed. |
|
*/ |
|
tooltipSkipNull: true, |
|
|
|
/* |
|
* @cfg {String} [tooltipPrefix] A string to prepend to each field displayed in a tooltip. |
|
*/ |
|
tooltipPrefix: '', |
|
|
|
/* |
|
* @cfg {String} [tooltipSuffix] A string to append to each field displayed in a tooltip. |
|
*/ |
|
tooltipSuffix: '', |
|
|
|
/* |
|
* @cfg {Boolean} [disableTooltips=false] Set to `true` to disable mouseover tooltips. |
|
*/ |
|
disableTooltips: false, |
|
|
|
disableInteraction: false, |
|
|
|
/** |
|
* @cfg {String/Ext.XTemplate} [tipTpl] An XTemplate used to display the value or values in a tooltip when hovering over a Sparkline. |
|
* |
|
* The implemented subclases all define their own `tipTpl`, but it can be overridden. |
|
*/ |
|
tipTpl: null |
|
}, |
|
|
|
config: { |
|
/** |
|
* @cfg {Number[]} values An array of numbers which define the chart. |
|
*/ |
|
values: null |
|
}, |
|
|
|
element: { |
|
tag: 'canvas', |
|
reference: 'element', |
|
style: { |
|
display: 'inline-block', |
|
verticalAlign: 'top' |
|
}, |
|
listeners: { |
|
mouseenter: 'onMouseEnter', |
|
mouseleave: 'onMouseLeave', |
|
mousemove: 'onMouseMove' |
|
}, |
|
// Create canvas zero sized so that it does not affect the containing element's initial layout |
|
// https://sencha.jira.com/browse/EXTJSIV-10145 |
|
width: 0, |
|
height: 0 |
|
}, |
|
|
|
defaultBindProperty: 'values', |
|
|
|
// When any config is changed, the canvas needs to be redrawn. |
|
// This is done at the next animation frame when this queue is traversed. |
|
redrawQueue: {}, |
|
|
|
inheritableStatics: { |
|
sparkLineTipClass: Ext.baseCSSPrefix + 'sparkline-tip-target', |
|
|
|
onClassCreated: function(cls) { |
|
var proto = cls.prototype, |
|
configs = cls.getConfigurator().configs, |
|
config; |
|
|
|
// Set up an applier for all local configs which kicks off a request to redraw on the next animation frame |
|
for (config in configs) { |
|
// tipTpl not included in this scheme |
|
if (config !== 'tipTpl') { |
|
proto[Ext.Config.get(config).names.apply] = proto.applyConfigChange; |
|
} |
|
} |
|
} |
|
}, |
|
|
|
constructor: function(config) { |
|
var me = this; |
|
|
|
// The canvas sets our element config |
|
me.canvas = Ext.supports.Canvas ? new Ext.sparkline.CanvasCanvas(me) |
|
: new Ext.sparkline.VmlCanvas(me); |
|
if (!me.getDisableTooltips()) { |
|
me.element.cls = Ext.sparkline.Base.sparkLineTipClass; |
|
} |
|
|
|
Ext.apply(me, config); |
|
me.callParent([config]); |
|
|
|
// For compatibility of all the code. |
|
me.el = me.element; |
|
}, |
|
|
|
// determine if all values of an array match a value |
|
// returns true if the array is empty |
|
all: function (val, arr, ignoreNull) { |
|
var i; |
|
for (i = arr.length; i--; ) { |
|
if (ignoreNull && arr[i] === null) { |
|
continue; |
|
} |
|
if (arr[i] !== val) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
}, |
|
|
|
// generic config value applier. |
|
// Adds this to the queue to do a redraw on the next animation frame |
|
applyConfigChange: function(newValue) { |
|
var me = this; |
|
me.redrawQueue[me.getId()] = me; |
|
|
|
// Ensure that there is a single timer to handle all queued redraws. |
|
if (!me.redrawTimer) { |
|
Ext.sparkline.Base.prototype.redrawTimer = |
|
Ext.Function.requestAnimationFrame(me.processRedrawQueue); |
|
} |
|
return newValue; |
|
}, |
|
|
|
// Appliers convert an incoming config value. |
|
// Ensure the tipTpl is an XTemplate |
|
applyTipTpl: function(tipTpl) { |
|
if (!tipTpl.isTemplate) { |
|
tipTpl = new Ext.XTemplate(tipTpl); |
|
} |
|
return tipTpl; |
|
}, |
|
|
|
normalizeValue: function (val) { |
|
var nf; |
|
switch (val) { |
|
case 'undefined': |
|
val = undefined; |
|
break; |
|
case 'null': |
|
val = null; |
|
break; |
|
case 'true': |
|
val = true; |
|
break; |
|
case 'false': |
|
val = false; |
|
break; |
|
default: |
|
nf = parseFloat(val); |
|
if (val == nf) { |
|
val = nf; |
|
} |
|
} |
|
return val; |
|
}, |
|
|
|
normalizeValues: function (vals) { |
|
var i, result = []; |
|
for (i = vals.length; i--;) { |
|
result[i] = this.normalizeValue(vals[i]); |
|
} |
|
return result; |
|
}, |
|
|
|
doSetWidth: function(width) { |
|
var me = this, |
|
dom = me.element.dom; |
|
|
|
me.callParent(arguments); |
|
me.canvas.setWidth(width); |
|
me.width = width; |
|
if (me.height == null) { |
|
me.setHeight(parseInt(me.measurer.getCachedStyle(dom.parentNode, 'line-height'), 10)); |
|
} |
|
else { |
|
me.redrawQueue[me.getId()] = me; |
|
} |
|
}, |
|
|
|
doSetHeight: function(height) { |
|
var me = this; |
|
|
|
me.callParent(arguments); |
|
me.canvas.setHeight(height); |
|
me.height = height; |
|
me.redrawQueue[me.getId()] = me; |
|
}, |
|
|
|
updateValues: function(values) { |
|
this.values = values; |
|
}, |
|
|
|
redraw: function() { |
|
var me = this, |
|
tooltip; |
|
|
|
if (me.getValues()) { |
|
tooltip = me.tooltip; |
|
// Avoid the visible tooltup thinking a subsequent mousemove is a mouseout by updating its triggerElement |
|
if (tooltip && tooltip.isVisible() && me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) { |
|
me.tooltip.triggerElement = me.el.dom; |
|
} |
|
|
|
me.onUpdate(); |
|
me.canvas.onOwnerUpdate(); |
|
me.renderGraph(); |
|
} |
|
}, |
|
|
|
onUpdate: Ext.emptyFn, |
|
|
|
/* |
|
* Render the chart to the canvas |
|
*/ |
|
renderGraph: function () { |
|
var ret = true; |
|
if (this.disabled) { |
|
this.canvas.reset(); |
|
ret = false; |
|
} |
|
return ret; |
|
}, |
|
|
|
onMouseEnter: function(e) { |
|
this.onMouseMove(e); |
|
}, |
|
|
|
onMouseMove: function (e) { |
|
this.currentPageXY = e.getPoint(); |
|
this.redraw(); |
|
}, |
|
|
|
onMouseLeave: function () { |
|
var me = this; |
|
me.currentPageXY = me.targetX = me.targetY = null; |
|
me.redraw(); |
|
}, |
|
|
|
updateDisplay: function () { |
|
var me = this, |
|
offset, tooltip, tooltipHTML, region; |
|
|
|
if (me.currentPageXY && me.el.getRegion().contains(me.currentPageXY)) { |
|
offset = me.canvas.el.getXY(); |
|
region = me.getRegion(me.currentPageXY[0] - offset[0], me.currentPageXY[1] - offset[1]); |
|
|
|
if (region != null && !me.disableHighlight) { |
|
me.renderHighlight(region); |
|
} |
|
me.fireEvent('sparklineregionchange', me); |
|
|
|
tooltip = me.tooltip; |
|
if (region != null && tooltip) { |
|
tooltipHTML = me.getRegionTooltip(region); |
|
if (tooltipHTML) { |
|
if (!me.lastTooltipHTML || tooltipHTML[0] !== me.lastTooltipHTML[0] || tooltipHTML[1] !== me.lastTooltipHTML[1]) { |
|
tooltip.setTitle(tooltipHTML[0]); |
|
tooltip.update(tooltipHTML[1]); |
|
me.lastTooltipHTML = tooltipHTML; |
|
} |
|
} else { |
|
tooltip.hide(); |
|
} |
|
} |
|
} |
|
}, |
|
|
|
/* |
|
* Return a region id for a given x/y co-ordinate |
|
*/ |
|
getRegion: Ext.emptyFn, |
|
|
|
/* |
|
* Fetch the HTML to display as a tooltip |
|
*/ |
|
getRegionTooltip: function(region) { |
|
var me = this, |
|
header = me.tooltipChartTitle, |
|
entries = [], |
|
tipTpl = me.getTipTpl(), |
|
fields, showFields, showFieldsKey, newFields, fv, |
|
formatter, fieldlen, i, j; |
|
|
|
fields = me.getRegionFields(region); |
|
formatter = me.tooltipFormatter; |
|
if (formatter) { |
|
return formatter(me, me, fields); |
|
} |
|
|
|
if (!tipTpl) { |
|
return ''; |
|
} |
|
if (!Ext.isArray(fields)) { |
|
fields = [fields]; |
|
} |
|
showFields = me.tooltipFormatFieldlist; |
|
showFieldsKey = me.tooltipFormatFieldlistKey; |
|
if (showFields && showFieldsKey) { |
|
// user-selected ordering of fields |
|
newFields = []; |
|
for (i = fields.length; i--;) { |
|
fv = fields[i][showFieldsKey]; |
|
if ((j = Ext.Array.indexOf(fv, showFields)) !== -1) { |
|
newFields[j] = fields[i]; |
|
} |
|
} |
|
fields = newFields; |
|
} |
|
fieldlen = fields.length; |
|
|
|
for (j = 0; j < fieldlen; j++) { |
|
if (!fields[j].isNull || !me.getTooltipSkipNull()) { |
|
Ext.apply(fields[j], { |
|
prefix: me.getTooltipPrefix(), |
|
suffix: me.getTooltipSuffix() |
|
}); |
|
entries.push(tipTpl.apply(fields[j])); |
|
} |
|
} |
|
|
|
if (header || entries.length) { |
|
return [header, entries.join('<br>')]; |
|
} |
|
return ''; |
|
}, |
|
|
|
getRegionFields: Ext.emptyFn, |
|
|
|
calcHighlightColor: function(color) { |
|
var me = this, |
|
highlightColor = me.getHighlightColor(), |
|
lighten = me.getHighlightLighten(), |
|
parse, mult, rgbnew, i; |
|
|
|
if (highlightColor) { |
|
return highlightColor; |
|
} |
|
if (lighten) { |
|
// extract RGB values |
|
parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color); |
|
if (parse) { |
|
rgbnew = []; |
|
mult = color.length === 4 ? 16 : 1; |
|
for (i = 0; i < 3; i++) { |
|
rgbnew[i] = Ext.Number.constrain(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255); |
|
} |
|
return 'rgb(' + rgbnew.join(',') + ')'; |
|
} |
|
} |
|
return color; |
|
}, |
|
|
|
destroy: function() { |
|
delete this.redrawQueue[this.getId()]; |
|
this.callParent(); |
|
} |
|
}, function(SparklineBase) { |
|
var proto = SparklineBase.prototype; |
|
|
|
Ext.onInternalReady(function() { |
|
proto.tooltip = new Ext.tip.ToolTip({ |
|
id: 'sparklines-tooltip', |
|
target: document.body, |
|
delegate: '.' + SparklineBase.sparkLineTipClass, |
|
showDelay: 0, |
|
dismissDelay: 0, |
|
hideDelay: 400 |
|
}); |
|
}); |
|
|
|
SparklineBase.onClassCreated(SparklineBase); |
|
|
|
proto.processRedrawQueue = function () { |
|
var redrawQueue = proto.redrawQueue, |
|
id; |
|
|
|
for (id in redrawQueue) { |
|
redrawQueue[id].redraw(); |
|
} |
|
proto.redrawQueue = {}; |
|
proto.redrawTimer = 0; |
|
}; |
|
|
|
// If we are on a VML platform (IE8 - TODO: remove this when that retires)... |
|
if (!Ext.supports.Canvas) { |
|
SparklineBase.prototype.element = { |
|
tag: 'span', |
|
reference: 'element', |
|
listeners: { |
|
mouseenter: 'onMouseEnter', |
|
mouseleave: 'onMouseLeave', |
|
mousemove: 'onMouseMove' |
|
}, |
|
style: { |
|
display: 'inline-block', |
|
position: 'relative', |
|
overflow: 'hidden', |
|
margin: '0px', |
|
padding: '0px', |
|
verticalAlign: 'top', |
|
cursor: 'default' |
|
}, |
|
children: [{ |
|
tag: 'svml:group', |
|
reference: 'groupEl', |
|
coordorigin: '0 0', |
|
coordsize: '0 0', |
|
style: 'position:absolute;width:0;height:0;pointer-events:none' |
|
}] |
|
}; |
|
} |
|
});
|
|
|