Files
grafana/packages/grafana-ui/src/components/Gauge/Gauge.tsx

192 lines
5.3 KiB
TypeScript
Raw Normal View History

2018-11-08 16:40:53 +01:00
import React, { PureComponent } from 'react';
import $ from 'jquery';
2019-03-13 11:12:11 -07:00
import { getColorFromHexRgbOrName } from '../../utils';
import { DisplayValue, Threshold, Themeable } from '../../types';
import { selectThemeVariant } from '../../themes';
export interface Props extends Themeable {
2018-12-11 16:44:42 +01:00
height: number;
maxValue: number;
minValue: number;
2018-11-20 17:01:58 +01:00
thresholds: Threshold[];
2018-12-11 16:44:42 +01:00
showThresholdMarkers: boolean;
showThresholdLabels: boolean;
2018-12-11 16:44:42 +01:00
width: number;
2019-03-11 14:47:54 -07:00
value: DisplayValue;
2018-11-08 16:40:53 +01:00
}
2019-01-25 16:38:51 +01:00
const FONT_SCALE = 1;
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: Partial<Props> = {
2018-11-08 16:40:53 +01:00
maxValue: 100,
2018-12-11 16:44:42 +01:00
minValue: 0,
2018-11-08 16:40:53 +01:00
showThresholdMarkers: true,
showThresholdLabels: false,
2018-12-14 13:23:22 +01:00
thresholds: [],
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
}
getFormattedThresholds() {
const { maxValue, minValue, thresholds, theme } = this.props;
const lastThreshold = thresholds[thresholds.length - 1];
2019-01-25 16:38:51 +01:00
return [
...thresholds.map(threshold => {
if (threshold.index === 0) {
return { value: minValue, color: getColorFromHexRgbOrName(threshold.color, theme.type) };
}
const previousThreshold = thresholds[threshold.index - 1];
return { value: threshold.value, color: getColorFromHexRgbOrName(previousThreshold.color, theme.type) };
}),
{ value: maxValue, color: getColorFromHexRgbOrName(lastThreshold.color, theme.type) },
];
2019-01-25 16:38:51 +01:00
}
2019-01-25 16:38:51 +01:00
getFontScale(length: number): number {
if (length > 12) {
return FONT_SCALE - (length * 5) / 110;
2019-01-25 16:38:51 +01:00
}
return FONT_SCALE - (length * 5) / 100;
}
2018-11-08 16:40:53 +01:00
draw() {
const { maxValue, minValue, showThresholdLabels, showThresholdMarkers, width, height, theme, value } = this.props;
2018-11-29 15:23:16 +01:00
const autoProps = calculateGaugeAutoProps(width, height, value.title);
const dimension = Math.min(width, autoProps.gaugeHeight);
const backgroundColor = selectThemeVariant(
{
dark: theme.colors.dark8,
light: theme.colors.gray6,
},
theme.type
);
2018-12-03 14:37:42 +01:00
const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
const gaugeWidth = Math.min(dimension / 5, 40) / gaugeWidthReduceRatio;
2018-11-09 16:37:09 +01:00
const thresholdMarkersWidth = gaugeWidth / 5;
const fontSize = Math.min(dimension / 5.5, 100) * (value.text !== null ? this.getFontScale(value.text.length) : 1);
2018-11-09 16:37:09 +01:00
const thresholdLabelFontSize = fontSize / 2.5;
const options: any = {
2018-11-08 16:40:53 +01:00
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, vMargin: 0 },
2018-11-08 16:40:53 +01:00
cell: { border: { width: 0 } },
threshold: {
values: this.getFormattedThresholds(),
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: {
2019-03-13 11:12:11 -07:00
color: value.color,
2018-11-08 16:40:53 +01:00
formatter: () => {
2019-03-11 14:47:54 -07:00
return value.text;
2018-11-08 16:40:53 +01:00
},
font: { size: fontSize, family: theme.typography.fontFamily.sansSerif },
2018-11-08 16:40:53 +01:00
},
show: true,
},
},
};
const plotSeries = {
data: [[0, value.numeric]],
label: value.title,
};
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, value);
2018-11-08 16:40:53 +01:00
}
}
render() {
const { width, value, height } = this.props;
const autoProps = calculateGaugeAutoProps(width, height, value.title);
2018-11-09 16:37:09 +01:00
2018-11-08 16:40:53 +01:00
return (
2019-02-05 15:12:04 +01:00
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
overflow: 'hidden',
2019-02-05 15:12:04 +01:00
}}
>
<div
style={{ height: `${autoProps.gaugeHeight}px`, width: '100%' }}
ref={element => (this.canvasElement = element)}
/>
{autoProps.showLabel && (
<div
style={{
textAlign: 'center',
fontSize: autoProps.titleFontSize,
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
position: 'relative',
width: '100%',
top: '-4px',
}}
>
{value.title}
</div>
)}
</div>
2018-11-08 16:40:53 +01:00
);
}
}
interface GaugeAutoProps {
titleFontSize: number;
gaugeHeight: number;
showLabel: boolean;
}
function calculateGaugeAutoProps(width: number, height: number, title: string | undefined): GaugeAutoProps {
const showLabel = title !== null && title !== undefined;
const titleFontSize = Math.min((width * 0.15) / 1.5, 20); // 20% of height * line-height, max 40px
const titleHeight = titleFontSize * 1.5;
const availableHeight = showLabel ? height - titleHeight : height;
const gaugeHeight = Math.min(availableHeight, width);
return {
showLabel,
gaugeHeight,
titleFontSize,
};
}