| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- import _ from 'lodash';
- import React, { PureComponent } from 'react';
- import { TimeSeries } from 'app/core/core';
- import { CustomScrollbar } from '@grafana/ui';
- import { LegendItem, LEGEND_STATS } from './LegendSeriesItem';
- interface LegendProps {
- seriesList: TimeSeries[];
- optionalClass?: string;
- }
- interface LegendEventHandlers {
- onToggleSeries?: (hiddenSeries) => void;
- onToggleSort?: (sortBy, sortDesc) => void;
- onToggleAxis?: (series: TimeSeries) => void;
- onColorChange?: (series: TimeSeries, color: string) => void;
- }
- interface LegendComponentEventHandlers {
- onToggleSeries?: (series, event) => void;
- onToggleSort?: (sortBy, sortDesc) => void;
- onToggleAxis?: (series: TimeSeries) => void;
- onColorChange?: (series: TimeSeries, color: string) => void;
- }
- interface LegendDisplayProps {
- hiddenSeries: any;
- hideEmpty?: boolean;
- hideZero?: boolean;
- alignAsTable?: boolean;
- rightSide?: boolean;
- sideWidth?: number;
- }
- interface LegendValuesProps {
- values?: boolean;
- min?: boolean;
- max?: boolean;
- avg?: boolean;
- current?: boolean;
- total?: boolean;
- }
- interface LegendSortProps {
- sort?: 'min' | 'max' | 'avg' | 'current' | 'total';
- sortDesc?: boolean;
- }
- export type GraphLegendProps = LegendProps &
- LegendDisplayProps &
- LegendValuesProps &
- LegendSortProps &
- LegendEventHandlers;
- export type LegendComponentProps = LegendProps &
- LegendDisplayProps &
- LegendValuesProps &
- LegendSortProps &
- LegendComponentEventHandlers;
- interface LegendState {
- hiddenSeries: { [seriesAlias: string]: boolean };
- }
- export class GraphLegend extends PureComponent<GraphLegendProps, LegendState> {
- static defaultProps: Partial<GraphLegendProps> = {
- values: false,
- min: false,
- max: false,
- avg: false,
- current: false,
- total: false,
- alignAsTable: false,
- rightSide: false,
- sort: undefined,
- sortDesc: false,
- optionalClass: '',
- onToggleSeries: () => {},
- onToggleSort: () => {},
- onToggleAxis: () => {},
- onColorChange: () => {},
- };
- constructor(props) {
- super(props);
- this.state = {
- hiddenSeries: this.props.hiddenSeries,
- };
- }
- sortLegend() {
- let seriesList: TimeSeries[] = [...this.props.seriesList] || [];
- if (this.props.sort) {
- seriesList = _.sortBy(seriesList, series => {
- let sort = series.stats[this.props.sort];
- if (sort === null) {
- sort = -Infinity;
- }
- return sort;
- }) as TimeSeries[];
- if (this.props.sortDesc) {
- seriesList = seriesList.reverse();
- }
- }
- return seriesList;
- }
- onToggleSeries = (series, event) => {
- let hiddenSeries = { ...this.state.hiddenSeries };
- if (event.ctrlKey || event.metaKey || event.shiftKey) {
- if (hiddenSeries[series.alias]) {
- delete hiddenSeries[series.alias];
- } else {
- hiddenSeries[series.alias] = true;
- }
- } else {
- hiddenSeries = this.toggleSeriesExclusiveMode(series);
- }
- this.setState({ hiddenSeries: hiddenSeries });
- this.props.onToggleSeries(hiddenSeries);
- };
- toggleSeriesExclusiveMode(series) {
- const hiddenSeries = { ...this.state.hiddenSeries };
- if (hiddenSeries[series.alias]) {
- delete hiddenSeries[series.alias];
- }
- // check if every other series is hidden
- const alreadyExclusive = this.props.seriesList.every(value => {
- if (value.alias === series.alias) {
- return true;
- }
- return hiddenSeries[value.alias];
- });
- if (alreadyExclusive) {
- // remove all hidden series
- this.props.seriesList.forEach(value => {
- delete hiddenSeries[value.alias];
- });
- } else {
- // hide all but this serie
- this.props.seriesList.forEach(value => {
- if (value.alias === series.alias) {
- return;
- }
- hiddenSeries[value.alias] = true;
- });
- }
- return hiddenSeries;
- }
- render() {
- const {
- optionalClass,
- rightSide,
- sideWidth,
- sort,
- sortDesc,
- hideEmpty,
- hideZero,
- values,
- min,
- max,
- avg,
- current,
- total,
- } = this.props;
- const seriesValuesProps = { values, min, max, avg, current, total };
- const hiddenSeries = this.state.hiddenSeries;
- const seriesHideProps = { hideEmpty, hideZero };
- const sortProps = { sort, sortDesc };
- const seriesList = this.sortLegend().filter(series => !series.hideFromLegend(seriesHideProps));
- const legendClass = `${this.props.alignAsTable ? 'graph-legend-table' : ''} ${optionalClass}`;
- // Set min-width if side style and there is a value, otherwise remove the CSS property
- // Set width so it works with IE11
- const width: any = rightSide && sideWidth ? sideWidth : undefined;
- const ieWidth: any = rightSide && sideWidth ? sideWidth - 1 : undefined;
- const legendStyle: React.CSSProperties = {
- minWidth: width,
- width: ieWidth,
- };
- const legendProps: LegendComponentProps = {
- seriesList: seriesList,
- hiddenSeries: hiddenSeries,
- onToggleSeries: this.onToggleSeries,
- onToggleAxis: this.props.onToggleAxis,
- onToggleSort: this.props.onToggleSort,
- onColorChange: this.props.onColorChange,
- ...seriesValuesProps,
- ...sortProps,
- };
- return (
- <div className={`graph-legend-content ${legendClass}`} style={legendStyle}>
- {this.props.alignAsTable ? <LegendTable {...legendProps} /> : <LegendSeriesList {...legendProps} />}
- </div>
- );
- }
- }
- class LegendSeriesList extends PureComponent<LegendComponentProps> {
- render() {
- const { seriesList, hiddenSeries, values, min, max, avg, current, total } = this.props;
- const seriesValuesProps = { values, min, max, avg, current, total };
- return seriesList.map((series, i) => (
- <LegendItem
- // This trick required because TimeSeries.id is not unique (it's just TimeSeries.alias).
- // In future would be good to make id unique across the series list.
- key={`${series.id}-${i}`}
- series={series}
- hidden={hiddenSeries[series.alias]}
- {...seriesValuesProps}
- onLabelClick={this.props.onToggleSeries}
- onColorChange={this.props.onColorChange}
- onToggleAxis={this.props.onToggleAxis}
- />
- ));
- }
- }
- class LegendTable extends PureComponent<Partial<LegendComponentProps>> {
- onToggleSort = stat => {
- let sortDesc = this.props.sortDesc;
- let sortBy = this.props.sort;
- if (stat !== sortBy) {
- sortDesc = null;
- }
- // if already sort ascending, disable sorting
- if (sortDesc === false) {
- sortBy = null;
- sortDesc = null;
- } else {
- sortDesc = !sortDesc;
- sortBy = stat;
- }
- this.props.onToggleSort(sortBy, sortDesc);
- };
- render() {
- const seriesList = this.props.seriesList;
- const { values, min, max, avg, current, total, sort, sortDesc, hiddenSeries } = this.props;
- const seriesValuesProps = { values, min, max, avg, current, total };
- return (
- <table>
- <colgroup>
- <col style={{ width: '100%' }} />
- </colgroup>
- <thead>
- <tr>
- <th style={{ textAlign: 'left' }} />
- {LEGEND_STATS.map(
- statName =>
- seriesValuesProps[statName] && (
- <LegendTableHeaderItem
- key={statName}
- statName={statName}
- sort={sort}
- sortDesc={sortDesc}
- onClick={this.onToggleSort}
- />
- )
- )}
- </tr>
- </thead>
- <tbody>
- {seriesList.map((series, i) => (
- <LegendItem
- key={`${series.id}-${i}`}
- asTable={true}
- series={series}
- hidden={hiddenSeries[series.alias]}
- onLabelClick={this.props.onToggleSeries}
- onColorChange={this.props.onColorChange}
- onToggleAxis={this.props.onToggleAxis}
- {...seriesValuesProps}
- />
- ))}
- </tbody>
- </table>
- );
- }
- }
- interface LegendTableHeaderProps {
- statName: string;
- onClick?: (statName: string) => void;
- }
- class LegendTableHeaderItem extends PureComponent<LegendTableHeaderProps & LegendSortProps> {
- onClick = () => this.props.onClick(this.props.statName);
- render() {
- const { statName, sort, sortDesc } = this.props;
- return (
- <th className="pointer" onClick={this.onClick}>
- {statName}
- {sort === statName && <span className={sortDesc ? 'fa fa-caret-down' : 'fa fa-caret-up'} />}
- </th>
- );
- }
- }
- export class Legend extends PureComponent<GraphLegendProps> {
- render() {
- return (
- <CustomScrollbar renderTrackHorizontal={props => <div {...props} style={{ visibility: 'none' }} />}>
- <GraphLegend {...this.props} />
- </CustomScrollbar>
- );
- }
- }
- export default Legend;
|