mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #14023 from grafana/gauge-panel
React Gauge Panel - first step
This commit is contained in:
commit
a2a4458923
@ -24,6 +24,7 @@ import * as heatmapPanel from 'app/plugins/panel/heatmap/module';
|
||||
import * as tablePanel from 'app/plugins/panel/table/module';
|
||||
import * as singlestatPanel from 'app/plugins/panel/singlestat/module';
|
||||
import * as gettingStartedPanel from 'app/plugins/panel/gettingstarted/module';
|
||||
import * as gaugePanel from 'app/plugins/panel/gauge/module';
|
||||
|
||||
const builtInPlugins = {
|
||||
'app/plugins/datasource/graphite/module': graphitePlugin,
|
||||
@ -52,6 +53,7 @@ const builtInPlugins = {
|
||||
'app/plugins/panel/table/module': tablePanel,
|
||||
'app/plugins/panel/singlestat/module': singlestatPanel,
|
||||
'app/plugins/panel/gettingstarted/module': gettingStartedPanel,
|
||||
'app/plugins/panel/gauge/module': gaugePanel,
|
||||
};
|
||||
|
||||
export default builtInPlugins;
|
||||
|
23
public/app/plugins/panel/gauge/module.tsx
Normal file
23
public/app/plugins/panel/gauge/module.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import Gauge from 'app/viz/Gauge';
|
||||
import { NullValueMode, PanelProps } from 'app/types';
|
||||
import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
|
||||
|
||||
export interface Options {}
|
||||
|
||||
interface Props extends PanelProps<Options> {}
|
||||
|
||||
export class GaugePanel extends PureComponent<Props> {
|
||||
render() {
|
||||
const { timeSeries } = this.props;
|
||||
|
||||
const vmSeries = getTimeSeriesVMs({
|
||||
timeSeries: timeSeries,
|
||||
nullValueMode: NullValueMode.Ignore,
|
||||
});
|
||||
|
||||
return <Gauge maxValue={100} minValue={0} timeSeries={vmSeries} thresholds={[0, 100]} />;
|
||||
}
|
||||
}
|
||||
|
||||
export { GaugePanel as PanelComponent };
|
18
public/app/plugins/panel/gauge/plugin.json
Normal file
18
public/app/plugins/panel/gauge/plugin.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Gauge",
|
||||
"id": "gauge",
|
||||
|
||||
"state": "alpha",
|
||||
|
||||
"info": {
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
},
|
||||
"logos": {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
133
public/app/viz/Gauge.tsx
Normal file
133
public/app/viz/Gauge.tsx
Normal file
@ -0,0 +1,133 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import $ from 'jquery';
|
||||
import { withSize } from 'react-sizeme';
|
||||
import { TimeSeriesVMs } from 'app/types';
|
||||
import config from '../core/config';
|
||||
|
||||
interface Props {
|
||||
timeSeries: TimeSeriesVMs;
|
||||
minValue: number;
|
||||
maxValue: number;
|
||||
showThresholdMarkers?: boolean;
|
||||
thresholds?: number[];
|
||||
showThresholdLables?: boolean;
|
||||
size?: { width: number; height: number };
|
||||
}
|
||||
|
||||
const colors = ['rgba(50, 172, 45, 0.97)', 'rgba(237, 129, 40, 0.89)', 'rgba(245, 54, 54, 0.9)'];
|
||||
|
||||
export class Gauge extends PureComponent<Props> {
|
||||
parentElement: any;
|
||||
canvasElement: any;
|
||||
|
||||
static defaultProps = {
|
||||
minValue: 0,
|
||||
maxValue: 100,
|
||||
showThresholdMarkers: true,
|
||||
showThresholdLables: false,
|
||||
thresholds: [],
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.draw();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
this.draw();
|
||||
}
|
||||
|
||||
draw() {
|
||||
const { maxValue, minValue, showThresholdLables, size, showThresholdMarkers, timeSeries, thresholds } = this.props;
|
||||
|
||||
const width = size.width;
|
||||
const height = size.height;
|
||||
const dimension = Math.min(width, height * 1.3);
|
||||
|
||||
const backgroundColor = config.bootData.user.lightTheme ? 'rgb(230,230,230)' : 'rgb(38,38,38)';
|
||||
const fontColor = config.bootData.user.lightTheme ? 'rgb(38,38,38)' : 'rgb(230,230,230)';
|
||||
const fontScale = parseInt('80', 10) / 100;
|
||||
const fontSize = Math.min(dimension / 5, 100) * fontScale;
|
||||
const gaugeWidth = Math.min(dimension / 6, 60);
|
||||
const thresholdMarkersWidth = gaugeWidth / 5;
|
||||
const thresholdLabelFontSize = fontSize / 2.5;
|
||||
|
||||
const formattedThresholds = [];
|
||||
|
||||
thresholds.forEach((threshold, index) => {
|
||||
formattedThresholds.push({
|
||||
value: threshold,
|
||||
color: colors[index],
|
||||
});
|
||||
});
|
||||
|
||||
const options = {
|
||||
series: {
|
||||
gauges: {
|
||||
gauge: {
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
background: { color: backgroundColor },
|
||||
border: { color: null },
|
||||
shadow: { show: false },
|
||||
width: gaugeWidth,
|
||||
},
|
||||
frame: { show: false },
|
||||
label: { show: false },
|
||||
layout: { margin: 0, thresholdWidth: 0 },
|
||||
cell: { border: { width: 0 } },
|
||||
threshold: {
|
||||
values: formattedThresholds,
|
||||
label: {
|
||||
show: showThresholdLables,
|
||||
margin: thresholdMarkersWidth + 1,
|
||||
font: { size: thresholdLabelFontSize },
|
||||
},
|
||||
show: showThresholdMarkers,
|
||||
width: thresholdMarkersWidth,
|
||||
},
|
||||
value: {
|
||||
color: fontColor,
|
||||
formatter: () => {
|
||||
return Math.round(timeSeries[0].stats.avg);
|
||||
},
|
||||
font: {
|
||||
size: fontSize,
|
||||
family: '"Helvetica Neue", Helvetica, Arial, sans-serif',
|
||||
},
|
||||
},
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const plotSeries = {
|
||||
data: [[0, timeSeries[0].stats.avg]],
|
||||
};
|
||||
|
||||
try {
|
||||
$.plot(this.canvasElement, [plotSeries], options);
|
||||
} catch (err) {
|
||||
console.log('Gauge rendering error', err, options, timeSeries);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { height, width } = this.props.size;
|
||||
|
||||
return (
|
||||
<div className="singlestat-panel" ref={element => (this.parentElement = element)}>
|
||||
<div
|
||||
style={{
|
||||
height: `${height * 0.9}px`,
|
||||
width: `${Math.min(width, height * 1.3)}px`,
|
||||
top: '10px',
|
||||
margin: 'auto',
|
||||
}}
|
||||
ref={element => (this.canvasElement = element)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withSize({ monitorHeight: true })(Gauge);
|
16
public/app/viz/GaugeOptions.tsx
Normal file
16
public/app/viz/GaugeOptions.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { PanelOptionsProps } from '../types';
|
||||
|
||||
interface Props {}
|
||||
|
||||
export class GaugeOptions extends PureComponent<PanelOptionsProps<Props>> {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div className="section gf-form-group">
|
||||
<h5 className="page-heading">Draw Modes</h5>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
37
public/vendor/flot/jquery.flot.gauge.js
vendored
37
public/vendor/flot/jquery.flot.gauge.js
vendored
@ -583,30 +583,31 @@
|
||||
* @param {Number} [a] the angle of the value drawn
|
||||
*/
|
||||
function drawText(x, y, id, text, textOptions, a) {
|
||||
var span = $("." + id, placeholder);
|
||||
var span = $(placeholder).find("#" + id);
|
||||
var exists = span.length;
|
||||
if (!exists) {
|
||||
span = $("<span></span>")
|
||||
span.attr("id", id);
|
||||
span.css("position", "absolute");
|
||||
span.css("top", y + "px");
|
||||
if (textOptions.font.size) {
|
||||
span.css("font-size", textOptions.font.size + "px");
|
||||
}
|
||||
if (textOptions.font.family) {
|
||||
span.css("font-family", textOptions.font.family);
|
||||
}
|
||||
if (textOptions.color) {
|
||||
span.css("color", textOptions.color);
|
||||
}
|
||||
if (textOptions.background.color) {
|
||||
span.css("background-color", textOptions.background.color);
|
||||
}
|
||||
if (textOptions.background.opacity) {
|
||||
span.css("opacity", textOptions.background.opacity);
|
||||
}
|
||||
placeholder.append(span);
|
||||
}
|
||||
|
||||
span.css("position", "absolute");
|
||||
span.css("top", y + "px");
|
||||
if (textOptions.font.size) {
|
||||
span.css("font-size", textOptions.font.size + "px");
|
||||
}
|
||||
if (textOptions.font.family) {
|
||||
span.css("font-family", textOptions.font.family);
|
||||
}
|
||||
if (textOptions.color) {
|
||||
span.css("color", textOptions.color);
|
||||
}
|
||||
if (textOptions.background.color) {
|
||||
span.css("background-color", textOptions.background.color);
|
||||
}
|
||||
if (textOptions.background.opacity) {
|
||||
span.css("opacity", textOptions.background.opacity);
|
||||
}
|
||||
span.text(text);
|
||||
// after append, readjust the left position
|
||||
span.css("left", x + "px"); // for redraw, resetting the left position is needed here
|
||||
|
Loading…
Reference in New Issue
Block a user