Files
grafana/public/app/viz/Gauge.tsx

226 lines
5.5 KiB
TypeScript
Raw Normal View History

2018-11-08 16:40:53 +01:00
import React, { PureComponent } from 'react';
import $ from 'jquery';
import { BasicGaugeColor, MappingType, RangeMap, Threshold, ValueMap } from 'app/types';
import { TimeSeriesVMs } from '@grafana/ui';
2018-11-08 16:40:53 +01:00
import config from '../core/config';
2018-11-16 16:41:01 +01:00
import kbn from '../core/utils/kbn';
2018-11-08 16:40:53 +01:00
interface Props {
2018-12-13 16:46:10 +01:00
baseColor: string;
2018-11-19 15:50:51 +01:00
decimals: number;
2018-12-11 16:44:42 +01:00
height: number;
mappings: Array<RangeMap | ValueMap>;
maxValue: number;
minValue: number;
prefix: string;
2018-11-08 16:40:53 +01:00
timeSeries: TimeSeriesVMs;
2018-11-20 17:01:58 +01:00
thresholds: Threshold[];
2018-12-11 16:44:42 +01:00
showThresholdMarkers: boolean;
showThresholdLabels: boolean;
stat: string;
suffix: string;
2018-12-11 16:44:42 +01:00
unit: string;
width: number;
2018-11-08 16:40:53 +01:00
}
export class Gauge extends PureComponent<Props> {
2018-11-09 16:37:09 +01:00
canvasElement: any;
2018-11-08 16:40:53 +01:00
static defaultProps = {
2018-12-13 16:46:10 +01:00
baseColor: BasicGaugeColor.Green,
2018-11-08 16:40:53 +01:00
maxValue: 100,
2018-12-11 16:44:42 +01:00
mappings: [],
minValue: 0,
2018-11-19 16:00:18 +01:00
prefix: '',
2018-11-08 16:40:53 +01:00
showThresholdMarkers: true,
showThresholdLabels: false,
suffix: '',
2018-12-14 13:23:22 +01:00
thresholds: [],
2018-12-11 16:44:42 +01:00
unit: 'none',
2018-12-13 16:46:10 +01:00
stat: 'avg',
2018-11-08 16:40:53 +01:00
};
componentDidMount() {
this.draw();
}
2018-11-29 15:23:16 +01:00
componentDidUpdate() {
2018-11-09 16:37:09 +01:00
this.draw();
2018-11-08 16:40:53 +01:00
}
2018-12-12 11:04:34 +01:00
formatWithMappings(mappings, value) {
const valueMaps = mappings.filter(m => m.type === MappingType.ValueToText);
const rangeMaps = mappings.filter(m => m.type === MappingType.RangeToText);
const valueMap = valueMaps.map(mapping => {
if (mapping.value && value === mapping.value) {
return mapping.text;
}
})[0];
const rangeMap = rangeMaps.map(mapping => {
if (mapping.from && mapping.to && value > mapping.from && value < mapping.to) {
return mapping.text;
}
})[0];
return {
rangeMap,
valueMap,
};
}
2018-11-16 16:41:01 +01:00
formatValue(value) {
2018-12-12 11:04:34 +01:00
const { decimals, mappings, prefix, suffix, unit } = this.props;
2018-11-16 16:41:01 +01:00
2018-11-19 15:50:51 +01:00
const formatFunc = kbn.valueFormats[unit];
2018-12-12 11:04:34 +01:00
const formattedValue = formatFunc(value, decimals);
if (mappings.length > 0) {
const { rangeMap, valueMap } = this.formatWithMappings(mappings, formattedValue);
if (valueMap) {
return valueMap;
} else if (rangeMap) {
return rangeMap;
}
}
if (isNaN(value)) {
return '-';
}
2018-11-16 16:41:01 +01:00
2018-12-12 11:04:34 +01:00
return `${prefix} ${formattedValue} ${suffix}`;
2018-11-16 16:41:01 +01:00
}
2018-12-13 16:46:10 +01:00
getFontColor(value) {
2018-12-17 14:26:19 +01:00
const { baseColor, maxValue, thresholds } = this.props;
2018-12-13 16:46:10 +01:00
2018-12-17 14:26:19 +01:00
const atThreshold = thresholds.filter(threshold => value <= threshold.value);
2018-12-13 16:46:10 +01:00
2018-12-17 14:26:19 +01:00
if (atThreshold.length > 0) {
return atThreshold[0].color;
} else if (value <= maxValue) {
return BasicGaugeColor.Red;
2018-12-13 16:46:10 +01:00
}
return baseColor;
}
2018-11-08 16:40:53 +01:00
draw() {
2018-12-13 16:46:10 +01:00
const {
2018-12-14 14:18:18 +01:00
baseColor,
2018-12-13 16:46:10 +01:00
maxValue,
minValue,
timeSeries,
showThresholdLabels,
showThresholdMarkers,
thresholds,
width,
height,
stat,
} = this.props;
let value: string | number = '';
if (timeSeries[0]) {
value = timeSeries[0].stats[stat];
} else {
value = 'N/A';
}
2018-11-29 15:23:16 +01:00
2018-11-09 16:37:09 +01:00
const dimension = Math.min(width, height * 1.3);
2018-11-08 16:40:53 +01:00
const backgroundColor = config.bootData.user.lightTheme ? 'rgb(230,230,230)' : 'rgb(38,38,38)';
2018-11-09 16:37:09 +01:00
const fontScale = parseInt('80', 10) / 100;
const fontSize = Math.min(dimension / 5, 100) * fontScale;
2018-12-03 14:37:42 +01:00
const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
const gaugeWidth = Math.min(dimension / 6, 60) / gaugeWidthReduceRatio;
2018-11-09 16:37:09 +01:00
const thresholdMarkersWidth = gaugeWidth / 5;
const thresholdLabelFontSize = fontSize / 2.5;
2018-12-14 13:23:22 +01:00
const formattedThresholds = [
{ value: minValue, color: BasicGaugeColor.Green },
...thresholds.map((threshold, index) => {
return {
value: threshold.value,
2018-12-17 14:26:19 +01:00
color: index === 0 ? threshold.color : thresholds[index].color,
2018-12-14 13:23:22 +01:00
};
}),
{
value: maxValue,
2018-12-14 14:18:18 +01:00
color: thresholds.length > 0 ? BasicGaugeColor.Red : baseColor,
2018-12-14 13:23:22 +01:00
},
];
2018-11-08 16:40:53 +01:00
const options = {
series: {
gauges: {
gauge: {
2018-12-13 16:46:10 +01:00
min: minValue,
max: maxValue,
2018-11-08 16:40:53 +01:00
background: { color: backgroundColor },
border: { color: null },
shadow: { show: false },
2018-11-09 16:37:09 +01:00
width: gaugeWidth,
2018-11-08 16:40:53 +01:00
},
frame: { show: false },
label: { show: false },
layout: { margin: 0, thresholdWidth: 0 },
cell: { border: { width: 0 } },
threshold: {
2018-11-09 16:37:09 +01:00
values: formattedThresholds,
2018-11-08 16:40:53 +01:00
label: {
show: showThresholdLabels,
2018-11-09 16:37:09 +01:00
margin: thresholdMarkersWidth + 1,
font: { size: thresholdLabelFontSize },
2018-11-08 16:40:53 +01:00
},
show: showThresholdMarkers,
2018-11-09 16:37:09 +01:00
width: thresholdMarkersWidth,
2018-11-08 16:40:53 +01:00
},
value: {
2018-12-13 16:46:10 +01:00
color: this.getFontColor(value),
2018-11-08 16:40:53 +01:00
formatter: () => {
2018-12-14 13:23:22 +01:00
return this.formatValue(value);
2018-11-08 16:40:53 +01:00
},
font: {
2018-11-09 16:37:09 +01:00
size: fontSize,
2018-11-08 16:40:53 +01:00
family: '"Helvetica Neue", Helvetica, Arial, sans-serif',
},
},
show: true,
},
},
};
2018-11-09 16:37:09 +01:00
const plotSeries = {
2018-11-14 13:20:19 +01:00
data: [[0, value]],
2018-11-09 16:37:09 +01:00
};
2018-11-08 16:40:53 +01:00
try {
2018-11-09 16:37:09 +01:00
$.plot(this.canvasElement, [plotSeries], options);
2018-11-08 16:40:53 +01:00
} catch (err) {
console.log('Gauge rendering error', err, options, timeSeries);
}
}
render() {
2018-11-14 13:20:19 +01:00
const { height, width } = this.props;
2018-11-09 16:37:09 +01:00
2018-11-08 16:40:53 +01:00
return (
<div className="singlestat-panel">
2018-11-09 16:37:09 +01:00
<div
style={{
height: `${height * 0.9}px`,
width: `${Math.min(width, height * 1.3)}px`,
top: '10px',
margin: 'auto',
}}
ref={element => (this.canvasElement = element)}
/>
2018-11-08 16:40:53 +01:00
</div>
);
}
}
2018-11-09 16:37:09 +01:00
2018-11-14 13:20:19 +01:00
export default Gauge;