Gauge.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import React, { PureComponent } from 'react';
  2. import $ from 'jquery';
  3. import { getColorFromHexRgbOrName } from '../../utils';
  4. import { DisplayValue, Threshold, GrafanaThemeType, Themeable } from '../../types';
  5. export interface Props extends Themeable {
  6. height: number;
  7. maxValue: number;
  8. minValue: number;
  9. thresholds: Threshold[];
  10. showThresholdMarkers: boolean;
  11. showThresholdLabels: boolean;
  12. width: number;
  13. value: DisplayValue;
  14. }
  15. const FONT_SCALE = 1;
  16. export class Gauge extends PureComponent<Props> {
  17. canvasElement: any;
  18. static defaultProps: Partial<Props> = {
  19. maxValue: 100,
  20. minValue: 0,
  21. showThresholdMarkers: true,
  22. showThresholdLabels: false,
  23. thresholds: [],
  24. };
  25. componentDidMount() {
  26. this.draw();
  27. }
  28. componentDidUpdate() {
  29. this.draw();
  30. }
  31. getFormattedThresholds() {
  32. const { maxValue, minValue, thresholds, theme } = this.props;
  33. const lastThreshold = thresholds[thresholds.length - 1];
  34. return [
  35. ...thresholds.map(threshold => {
  36. if (threshold.index === 0) {
  37. return { value: minValue, color: getColorFromHexRgbOrName(threshold.color, theme.type) };
  38. }
  39. const previousThreshold = thresholds[threshold.index - 1];
  40. return { value: threshold.value, color: getColorFromHexRgbOrName(previousThreshold.color, theme.type) };
  41. }),
  42. { value: maxValue, color: getColorFromHexRgbOrName(lastThreshold.color, theme.type) },
  43. ];
  44. }
  45. getFontScale(length: number): number {
  46. if (length > 12) {
  47. return FONT_SCALE - (length * 5) / 110;
  48. }
  49. return FONT_SCALE - (length * 5) / 100;
  50. }
  51. draw() {
  52. const { maxValue, minValue, showThresholdLabels, showThresholdMarkers, width, height, theme, value } = this.props;
  53. const dimension = Math.min(width, height * 1.3);
  54. const backgroundColor = theme.type === GrafanaThemeType.Light ? 'rgb(230,230,230)' : theme.colors.dark3;
  55. const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
  56. const gaugeWidth = Math.min(dimension / 6, 60) / gaugeWidthReduceRatio;
  57. const thresholdMarkersWidth = gaugeWidth / 5;
  58. const fontSize = Math.min(dimension / 5, 100) * (value.text !== null ? this.getFontScale(value.text.length) : 1);
  59. const thresholdLabelFontSize = fontSize / 2.5;
  60. const options: any = {
  61. series: {
  62. gauges: {
  63. gauge: {
  64. min: minValue,
  65. max: maxValue,
  66. background: { color: backgroundColor },
  67. border: { color: null },
  68. shadow: { show: false },
  69. width: gaugeWidth,
  70. },
  71. frame: { show: false },
  72. label: { show: false },
  73. layout: { margin: 0, thresholdWidth: 0 },
  74. cell: { border: { width: 0 } },
  75. threshold: {
  76. values: this.getFormattedThresholds(),
  77. label: {
  78. show: showThresholdLabels,
  79. margin: thresholdMarkersWidth + 1,
  80. font: { size: thresholdLabelFontSize },
  81. },
  82. show: showThresholdMarkers,
  83. width: thresholdMarkersWidth,
  84. },
  85. value: {
  86. color: value.color,
  87. formatter: () => {
  88. return value.text;
  89. },
  90. font: { size: fontSize, family: '"Helvetica Neue", Helvetica, Arial, sans-serif' },
  91. },
  92. show: true,
  93. },
  94. },
  95. };
  96. const plotSeries = { data: [[0, value.numeric]] };
  97. try {
  98. $.plot(this.canvasElement, [plotSeries], options);
  99. } catch (err) {
  100. console.log('Gauge rendering error', err, options, value);
  101. }
  102. }
  103. render() {
  104. const { height, width } = this.props;
  105. return (
  106. <div
  107. style={{
  108. height: `${Math.min(height, width * 1.3)}px`,
  109. width: `${Math.min(width, height * 1.3)}px`,
  110. top: '10px',
  111. margin: 'auto',
  112. }}
  113. ref={element => (this.canvasElement = element)}
  114. />
  115. );
  116. }
  117. }