mirror of
https://git.proxmox.com/git/proxmox-widget-toolkit
synced 2025-05-02 17:33:46 +00:00

in extjs 7.0, the marker does not have the 'fx' property anymore, but the now documented 'animation' property does not work as it seems. instead set the animation settings directly on the chart. The only downside is that this is now for *all* animations, not only the fade in of the highlight. (the default duration is 500ms) Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
272 lines
6.3 KiB
JavaScript
272 lines
6.3 KiB
JavaScript
Ext.define('Proxmox.chart.axis.segmenter.NumericBase2', {
|
|
extend: 'Ext.chart.axis.segmenter.Numeric',
|
|
alias: 'segmenter.numericBase2',
|
|
|
|
// derived from the original numeric segmenter but using 2 instead of 10 as base
|
|
preferredStep: function(min, estStepSize) {
|
|
// Getting an order of magnitude of the estStepSize with a common logarithm.
|
|
let order = Math.floor(Math.log2(estStepSize));
|
|
let scale = Math.pow(2, order);
|
|
|
|
estStepSize /= scale;
|
|
|
|
// FIXME: below is not useful when using base 2 instead of base 10, we could
|
|
// just directly set estStepSize to 2
|
|
if (estStepSize <= 1) {
|
|
estStepSize = 1;
|
|
} else if (estStepSize < 2) {
|
|
estStepSize = 2;
|
|
}
|
|
return {
|
|
unit: {
|
|
// When passed estStepSize is less than 1, its order of magnitude
|
|
// is equal to -number_of_leading_zeros in the estStepSize.
|
|
fixes: -order, // Number of fractional digits.
|
|
scale: scale,
|
|
},
|
|
step: estStepSize,
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Wraps the provided estimated step size of a range without altering it into a step size object.
|
|
*
|
|
* @param {*} min The start point of range.
|
|
* @param {*} estStepSize The estimated step size.
|
|
* @return {Object} Return the step size by an object of step x unit.
|
|
* @return {Number} return.step The step count of units.
|
|
* @return {Object} return.unit The unit.
|
|
*/
|
|
// derived from the original numeric segmenter but using 2 instead of 10 as base
|
|
exactStep: function(min, estStepSize) {
|
|
let order = Math.floor(Math.log2(estStepSize));
|
|
let scale = Math.pow(2, order);
|
|
|
|
return {
|
|
unit: {
|
|
// add one decimal point if estStepSize is not a multiple of scale
|
|
fixes: -order + (estStepSize % scale === 0 ? 0 : 1),
|
|
scale: 1,
|
|
},
|
|
step: estStepSize,
|
|
};
|
|
},
|
|
});
|
|
|
|
Ext.define('Proxmox.widget.RRDChart', {
|
|
extend: 'Ext.chart.CartesianChart',
|
|
alias: 'widget.proxmoxRRDChart',
|
|
|
|
unit: undefined, // bytes, bytespersecond, percent
|
|
|
|
powerOfTwo: false,
|
|
|
|
// set to empty string to suppress warning in debug mode
|
|
downloadServerUrl: '-',
|
|
|
|
controller: {
|
|
xclass: 'Ext.app.ViewController',
|
|
|
|
init: function(view) {
|
|
this.powerOfTwo = view.powerOfTwo;
|
|
},
|
|
|
|
convertToUnits: function(value) {
|
|
let units = ['', 'k', 'M', 'G', 'T', 'P'];
|
|
let si = 0;
|
|
let format = '0.##';
|
|
if (value < 0.1) format += '#';
|
|
const baseValue = this.powerOfTwo ? 1024 : 1000;
|
|
while (value >= baseValue && si < units.length -1) {
|
|
value = value / baseValue;
|
|
si++;
|
|
}
|
|
|
|
// javascript floating point weirdness
|
|
value = Ext.Number.correctFloat(value);
|
|
|
|
// limit decimal points
|
|
value = Ext.util.Format.number(value, format);
|
|
|
|
let unit = units[si];
|
|
if (this.powerOfTwo) unit += 'i';
|
|
|
|
return `${value.toString()} ${unit}`;
|
|
},
|
|
|
|
leftAxisRenderer: function(axis, label, layoutContext) {
|
|
let me = this;
|
|
return me.convertToUnits(label);
|
|
},
|
|
|
|
onSeriesTooltipRender: function(tooltip, record, item) {
|
|
let view = this.getView();
|
|
|
|
let suffix = '';
|
|
if (view.unit === 'percent') {
|
|
suffix = '%';
|
|
} else if (view.unit === 'bytes') {
|
|
suffix = 'B';
|
|
} else if (view.unit === 'bytespersecond') {
|
|
suffix = 'B/s';
|
|
}
|
|
|
|
let prefix = item.field;
|
|
if (view.fieldTitles && view.fieldTitles[view.fields.indexOf(item.field)]) {
|
|
prefix = view.fieldTitles[view.fields.indexOf(item.field)];
|
|
}
|
|
let v = this.convertToUnits(record.get(item.field));
|
|
let t = new Date(record.get('time'));
|
|
tooltip.setHtml(`${prefix}: ${v}${suffix}<br>${t}`);
|
|
},
|
|
|
|
onAfterAnimation: function(chart, eopts) {
|
|
if (!chart.header || !chart.header.tools) {
|
|
return;
|
|
}
|
|
// if the undo button is disabled, disable our tool
|
|
let ourUndoZoomButton = chart.header.tools[0];
|
|
let undoButton = chart.interactions[0].getUndoButton();
|
|
ourUndoZoomButton.setDisabled(undoButton.isDisabled());
|
|
},
|
|
},
|
|
|
|
width: 770,
|
|
height: 300,
|
|
animation: false,
|
|
interactions: [
|
|
{
|
|
type: 'crosszoom',
|
|
},
|
|
],
|
|
legend: {
|
|
type: 'dom',
|
|
padding: 0,
|
|
},
|
|
listeners: {
|
|
redraw: {
|
|
fn: 'onAfterAnimation',
|
|
options: {
|
|
buffer: 500,
|
|
},
|
|
},
|
|
},
|
|
|
|
constructor: function(config) {
|
|
let me = this;
|
|
|
|
let segmenter = config.powerOfTwo ? 'numericBase2' : 'numeric';
|
|
config.axes = [
|
|
{
|
|
type: 'numeric',
|
|
position: 'left',
|
|
grid: true,
|
|
renderer: 'leftAxisRenderer',
|
|
minimum: 0,
|
|
segmenter,
|
|
},
|
|
{
|
|
type: 'time',
|
|
position: 'bottom',
|
|
grid: true,
|
|
fields: ['time'],
|
|
},
|
|
];
|
|
me.callParent([config]);
|
|
},
|
|
|
|
initComponent: function() {
|
|
let me = this;
|
|
|
|
if (!me.store) {
|
|
throw "cannot work without store";
|
|
}
|
|
|
|
if (!me.fields) {
|
|
throw "cannot work without fields";
|
|
}
|
|
|
|
me.callParent();
|
|
|
|
// add correct label for left axis
|
|
let axisTitle = "";
|
|
if (me.unit === 'percent') {
|
|
axisTitle = "%";
|
|
} else if (me.unit === 'bytes') {
|
|
axisTitle = "Bytes";
|
|
} else if (me.unit === 'bytespersecond') {
|
|
axisTitle = "Bytes/s";
|
|
} else if (me.fieldTitles && me.fieldTitles.length === 1) {
|
|
axisTitle = me.fieldTitles[0];
|
|
} else if (me.fields.length === 1) {
|
|
axisTitle = me.fields[0];
|
|
}
|
|
|
|
me.axes[0].setTitle(axisTitle);
|
|
|
|
me.updateHeader();
|
|
|
|
if (me.header && me.legend) {
|
|
me.header.padding = '4 9 4';
|
|
me.header.add(me.legend);
|
|
me.legend = undefined;
|
|
}
|
|
|
|
if (!me.noTool) {
|
|
me.addTool({
|
|
type: 'minus',
|
|
disabled: true,
|
|
tooltip: gettext('Undo Zoom'),
|
|
handler: function() {
|
|
let undoButton = me.interactions[0].getUndoButton();
|
|
if (undoButton.handler) {
|
|
undoButton.handler();
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
// add a series for each field we get
|
|
me.fields.forEach(function(item, index) {
|
|
let title = item;
|
|
if (me.fieldTitles && me.fieldTitles[index]) {
|
|
title = me.fieldTitles[index];
|
|
}
|
|
me.addSeries(Ext.apply(
|
|
{
|
|
type: 'line',
|
|
xField: 'time',
|
|
yField: item,
|
|
title: title,
|
|
fill: true,
|
|
style: {
|
|
lineWidth: 1.5,
|
|
opacity: 0.60,
|
|
},
|
|
marker: {
|
|
opacity: 0,
|
|
scaling: 0.01,
|
|
},
|
|
highlightCfg: {
|
|
opacity: 1,
|
|
scaling: 1.5,
|
|
},
|
|
tooltip: {
|
|
trackMouse: true,
|
|
renderer: 'onSeriesTooltipRender',
|
|
},
|
|
},
|
|
me.seriesConfig,
|
|
));
|
|
});
|
|
|
|
// enable animation after the store is loaded
|
|
me.store.onAfter('load', function() {
|
|
me.setAnimation({
|
|
duration: 200,
|
|
easing: 'easeIn',
|
|
});
|
|
}, this, { single: true });
|
|
},
|
|
});
|