web/ui/react-app/src/pages/graph/Legend.tsx (65 lines of code) (raw):

import React, { PureComponent, SyntheticEvent } from 'react'; import SeriesName from './SeriesName'; import { GraphSeries } from './Graph'; interface LegendProps { chartData: GraphSeries[]; shouldReset: boolean; onLegendMouseOut: (ev: SyntheticEvent<HTMLDivElement>) => void; onSeriesToggle: (selected: number[], index: number) => void; onHover: (index: number) => (ev: SyntheticEvent<HTMLDivElement>) => void; } interface LegendState { selectedIndexes: number[]; } export class Legend extends PureComponent<LegendProps, LegendState> { state = { selectedIndexes: [] as number[], }; componentDidUpdate(prevProps: LegendProps) { if (this.props.shouldReset && prevProps.shouldReset !== this.props.shouldReset) { this.setState({ selectedIndexes: [] }); } } handleSeriesSelect = (index: number) => (ev: any) => { // TODO: add proper event type const { selectedIndexes } = this.state; let selected = [index]; if (ev.ctrlKey) { const { chartData } = this.props; if (selectedIndexes.includes(index)) { selected = selectedIndexes.filter(idx => idx !== index); } else { selected = // Flip the logic - In case none is selected ctrl + click should deselect clicked series. selectedIndexes.length === 0 ? chartData.reduce<number[]>((acc, _, i) => (i === index ? acc : [...acc, i]), []) : [...selectedIndexes, index]; // Select multiple. } } else if (selectedIndexes.length === 1 && selectedIndexes.includes(index)) { selected = []; } this.setState({ selectedIndexes: selected }); this.props.onSeriesToggle(selected, index); }; render() { const { chartData, onLegendMouseOut, onHover } = this.props; const { selectedIndexes } = this.state; const canUseHover = chartData.length > 1 && selectedIndexes.length === 0; return ( <div className="graph-legend" onMouseOut={canUseHover ? onLegendMouseOut : undefined}> {chartData.map(({ index, color, labels }) => ( <div style={{ opacity: selectedIndexes.length === 0 || selectedIndexes.includes(index) ? 1 : 0.5 }} onClick={chartData.length > 1 ? this.handleSeriesSelect(index) : undefined} onMouseOver={canUseHover ? onHover(index) : undefined} key={index} className="legend-item" > <span className="legend-swatch" style={{ backgroundColor: color }}></span> <SeriesName labels={labels} format /> </div> ))} {chartData.length > 1 && ( <div className="pl-1 mt-1 text-muted" style={{ fontSize: 13 }}> Click: select series, CTRL + click: toggle multiple series </div> )} </div> ); } }