1import React, { PureComponent, SyntheticEvent } from 'react';
2import SeriesName from './SeriesName';
3import { GraphSeries } from './Graph';
4
5interface LegendProps {
6  chartData: GraphSeries[];
7  shouldReset: boolean;
8  onLegendMouseOut: (ev: SyntheticEvent<HTMLDivElement>) => void;
9  onSeriesToggle: (selected: number[], index: number) => void;
10  onHover: (index: number) => (ev: SyntheticEvent<HTMLDivElement>) => void;
11}
12
13interface LegendState {
14  selectedIndexes: number[];
15}
16
17export class Legend extends PureComponent<LegendProps, LegendState> {
18  state = {
19    selectedIndexes: [] as number[],
20  };
21  componentDidUpdate(prevProps: LegendProps) {
22    if (this.props.shouldReset && prevProps.shouldReset !== this.props.shouldReset) {
23      this.setState({ selectedIndexes: [] });
24    }
25  }
26  handleSeriesSelect = (index: number) => (ev: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
27    // TODO: add proper event type
28    const { selectedIndexes } = this.state;
29
30    let selected = [index];
31    if (ev.ctrlKey || ev.metaKey) {
32      const { chartData } = this.props;
33      if (selectedIndexes.includes(index)) {
34        selected = selectedIndexes.filter(idx => idx !== index);
35      } else {
36        selected =
37          // Flip the logic - In case none is selected ctrl + click should deselect clicked series.
38          selectedIndexes.length === 0
39            ? chartData.reduce<number[]>((acc, _, i) => (i === index ? acc : [...acc, i]), [])
40            : [...selectedIndexes, index]; // Select multiple.
41      }
42    } else if (selectedIndexes.length === 1 && selectedIndexes.includes(index)) {
43      selected = [];
44    }
45
46    this.setState({ selectedIndexes: selected });
47    this.props.onSeriesToggle(selected, index);
48  };
49
50  render() {
51    const { chartData, onLegendMouseOut, onHover } = this.props;
52    const { selectedIndexes } = this.state;
53    const canUseHover = chartData.length > 1 && selectedIndexes.length === 0;
54
55    return (
56      <div className="graph-legend" onMouseOut={canUseHover ? onLegendMouseOut : undefined}>
57        {chartData.map(({ index, color, labels }) => (
58          <div
59            style={{ opacity: selectedIndexes.length === 0 || selectedIndexes.includes(index) ? 1 : 0.5 }}
60            onClick={chartData.length > 1 ? this.handleSeriesSelect(index) : undefined}
61            onMouseOver={canUseHover ? onHover(index) : undefined}
62            key={index}
63            className="legend-item"
64          >
65            <span className="legend-swatch" style={{ backgroundColor: color }}></span>
66            <SeriesName labels={labels} format />
67          </div>
68        ))}
69        {chartData.length > 1 && (
70          <div className="pl-1 mt-1 text-muted" style={{ fontSize: 13 }}>
71            Click: select series, {navigator.platform.includes('Mac') ? 'CMD' : 'CTRL'} + click: toggle multiple series
72          </div>
73        )}
74      </div>
75    );
76  }
77}
78