Prechádzať zdrojové kódy

graph: legend as React component

Alexander Zobnin 7 rokov pred
rodič
commit
0e10fdb415

+ 2 - 0
public/app/core/angular_wrappers.ts

@@ -6,6 +6,7 @@ import LoginBackground from './components/Login/LoginBackground';
 import { SearchResult } from './components/search/SearchResult';
 import { SearchResult } from './components/search/SearchResult';
 import { TagFilter } from './components/TagFilter/TagFilter';
 import { TagFilter } from './components/TagFilter/TagFilter';
 import DashboardPermissions from './components/Permissions/DashboardPermissions';
 import DashboardPermissions from './components/Permissions/DashboardPermissions';
+import { GraphLegend } from 'app/plugins/panel/graph/Legend';
 
 
 export function registerAngularDirectives() {
 export function registerAngularDirectives() {
   react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
   react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
@@ -19,4 +20,5 @@ export function registerAngularDirectives() {
     ['tagOptions', { watchDepth: 'reference' }],
     ['tagOptions', { watchDepth: 'reference' }],
   ]);
   ]);
   react2AngularDirective('dashboardPermissions', DashboardPermissions, ['backendSrv', 'dashboardId', 'folder']);
   react2AngularDirective('dashboardPermissions', DashboardPermissions, ['backendSrv', 'dashboardId', 'folder']);
+  react2AngularDirective('graphLegendReact', GraphLegend, ['seriesList', 'className']);
 }
 }

+ 189 - 0
public/app/plugins/panel/graph/Legend.tsx

@@ -0,0 +1,189 @@
+import _ from 'lodash';
+import React from 'react';
+
+const LEGEND_STATS = ['min', 'max', 'avg', 'current', 'total'];
+
+export interface GraphLegendProps {
+  seriesList: any[];
+  hiddenSeries: any;
+  values?: boolean;
+  min?: boolean;
+  max?: boolean;
+  avg?: boolean;
+  current?: boolean;
+  total?: boolean;
+  alignAsTable?: boolean;
+  rightSide?: boolean;
+  sideWidth?: number;
+  sort?: 'min' | 'max' | 'avg' | 'current' | 'total';
+  sortDesc?: boolean;
+  className?: string;
+}
+
+export interface GraphLegendState {}
+
+export class GraphLegend extends React.PureComponent<GraphLegendProps, GraphLegendState> {
+  sortLegend() {
+    let seriesList = this.props.seriesList || [];
+    if (this.props.sort) {
+      seriesList = _.sortBy(seriesList, function(series) {
+        let sort = series.stats[this.props.sort];
+        if (sort === null) {
+          sort = -Infinity;
+        }
+        return sort;
+      });
+      if (this.props.sortDesc) {
+        seriesList = seriesList.reverse();
+      }
+    }
+    return seriesList;
+  }
+
+  render() {
+    const { className = '', hiddenSeries } = this.props;
+    const { values, min, max, avg, current, total } = this.props;
+    const seriesValuesProps = { values, min, max, avg, current, total };
+    const seriesList = this.sortLegend();
+    return (
+      <div className={`${className} graph-legend`}>
+        <div className={`graph-legend-content ${this.props.alignAsTable ? 'graph-legend-table' : ''}`}>
+          <div className="graph-legend-scroll">
+            {this.props.alignAsTable ? (
+              <LegendTable seriesList={seriesList} hiddenSeries={hiddenSeries} {...seriesValuesProps} />
+            ) : (
+              seriesList.map((series, i) => (
+                <LegendSeriesItem
+                  key={series.id}
+                  series={series}
+                  index={i}
+                  hiddenSeries={hiddenSeries}
+                  {...seriesValuesProps}
+                />
+              ))
+            )}
+          </div>
+        </div>
+      </div>
+    );
+  }
+}
+
+interface LegendTableProps {
+  seriesList: any[];
+  hiddenSeries: any;
+  values?: boolean;
+  min?: boolean;
+  max?: boolean;
+  avg?: boolean;
+  current?: boolean;
+  total?: boolean;
+}
+
+class LegendTable extends React.PureComponent<LegendTableProps> {
+  render() {
+    const seriesList = this.props.seriesList;
+    const { values, min, max, avg, current, total } = this.props;
+    const seriesValuesProps = { values, min, max, avg, current, total };
+    const headerStyle: React.CSSProperties = {
+      textAlign: 'left',
+    };
+
+    return (
+      <tbody>
+        <tr>
+          <th colSpan={2} style={headerStyle} />
+          {LEGEND_STATS.map(
+            statName => seriesValuesProps[statName] && <LegendTableHeader key={statName} statName={statName} />
+          )}
+        </tr>
+        {seriesList.map((series, i) => (
+          <LegendSeriesItem
+            key={series.id}
+            series={series}
+            index={i}
+            hiddenSeries={this.props.hiddenSeries}
+            {...seriesValuesProps}
+          />
+        ))}
+      </tbody>
+    );
+  }
+}
+
+interface LegendTableHeaderProps {
+  statName: string;
+  sortDesc?: boolean;
+}
+
+function LegendTableHeader(props: LegendTableHeaderProps) {
+  return (
+    <th className="pointer" data-stat={props.statName}>
+      {props.statName}
+      <span className={props.sortDesc ? 'fa fa-caret-down' : 'fa fa-caret-up'} />
+    </th>
+  );
+}
+
+interface LegendSeriesItemProps {
+  series: any;
+  index: number;
+  hiddenSeries: any;
+  values?: boolean;
+  min?: boolean;
+  max?: boolean;
+  avg?: boolean;
+  current?: boolean;
+  total?: boolean;
+}
+
+class LegendSeriesItem extends React.Component<LegendSeriesItemProps> {
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    const { series, index, hiddenSeries } = this.props;
+    const seriesOptionClasses = getOptionSeriesCSSClasses(series, hiddenSeries);
+    const valueItems = this.props.values ? renderLegendValues(this.props, series) : [];
+    return (
+      <div className={`graph-legend-series ${seriesOptionClasses}`} data-series-index={index}>
+        <div className="graph-legend-icon">
+          <i className="fa fa-minus pointer" style={{ color: series.color }} />
+        </div>
+        <a className="graph-legend-alias pointer" title={series.aliasEscaped}>
+          {series.aliasEscaped}
+        </a>
+        {valueItems}
+      </div>
+    );
+  }
+}
+
+function LegendValue(props) {
+  const value = props.value;
+  const valueName = props.valueName;
+  return <div className={`graph-legend-value ${valueName}`}>{value}</div>;
+}
+
+function renderLegendValues(props: LegendSeriesItemProps, series) {
+  const legendValueItems = [];
+  for (const valueName of LEGEND_STATS) {
+    if (props[valueName]) {
+      const valueFormatted = series.formatValue(series.stats[valueName]);
+      legendValueItems.push(<LegendValue key={valueName} valueName={valueName} value={valueFormatted} />);
+    }
+  }
+  return legendValueItems;
+}
+
+function getOptionSeriesCSSClasses(series, hiddenSeries) {
+  const classes = [];
+  if (series.yaxis === 2) {
+    classes.push('graph-legend-series--right-y');
+  }
+  if (hiddenSeries[series.alias]) {
+    classes.push('graph-legend-series-hidden');
+  }
+  return classes.join(' ');
+}

+ 18 - 1
public/app/plugins/panel/graph/graph.ts

@@ -20,6 +20,9 @@ import { EventManager } from 'app/features/annotations/all';
 import { convertToHistogramData } from './histogram';
 import { convertToHistogramData } from './histogram';
 import { alignYLevel } from './align_yaxes';
 import { alignYLevel } from './align_yaxes';
 import config from 'app/core/config';
 import config from 'app/core/config';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { GraphLegend, GraphLegendProps } from './Legend';
 
 
 import { GraphCtrl } from './module';
 import { GraphCtrl } from './module';
 
 
@@ -82,7 +85,21 @@ class GraphElement {
     const graphHeight = this.elem.height();
     const graphHeight = this.elem.height();
     updateLegendValues(this.data, this.panel, graphHeight);
     updateLegendValues(this.data, this.panel, graphHeight);
 
 
-    this.ctrl.events.emit('render-legend');
+    // this.ctrl.events.emit('render-legend');
+    const { values, min, max, avg, current, total } = this.panel.legend;
+    const { alignAsTable, rightSide, sideWidth } = this.panel.legend;
+    const legendOptions = { alignAsTable, rightSide, sideWidth };
+    const valueOptions = { values, min, max, avg, current, total };
+    const legendProps: GraphLegendProps = {
+      seriesList: this.data,
+      hiddenSeries: this.ctrl.hiddenSeries,
+      ...legendOptions,
+      ...valueOptions,
+    };
+    const legendReactElem = React.createElement(GraphLegend, legendProps);
+    const legendElem = this.elem.parent().find('.graph-legend');
+    ReactDOM.render(legendReactElem, legendElem[0]);
+    this.onLegendRenderingComplete();
   }
   }
 
 
   onGraphHover(evt) {
   onGraphHover(evt) {