<commit_before>/* LineChart.tsx
 * ================
 *
 * A standard line chart.
 *
 */

import * as React from 'react'
import { last, guid, sortBy } from './Util'
import { computed, action, observable } from 'mobx'
import { observer } from 'mobx-react'
import { select } from 'd3-selection'
import { easeLinear } from 'd3-ease'

import { ChartConfig } from './ChartConfig'
import { Bounds } from './Bounds'
import { AxisBox } from './AxisBox'
import { StandardAxisBoxView } from './StandardAxisBoxView'
import { Lines } from './Lines'
import { HeightedLegend, HeightedLegendView } from './HeightedLegend'
import { ComparisonLine } from './ComparisonLine'
import { Tooltip } from './Tooltip'
import { NoData } from './NoData'
import { formatYear } from './Util'
import { ChartViewContext, ChartViewContextType } from './ChartViewContext'

export interface LineChartValue {
    x: number,
    y: number,
    time: number
}

export interface LineChartSeries {
    key: string,
    color: string,
    values: LineChartValue[],
    classed?: string,
    isProjection?: boolean,
    formatValue: (value: number) => string
}

@observer
export class LineChart extends React.Component<{ bounds: Bounds, chart: ChartConfig }> {
    base: React.RefObject<SVGGElement> = React.createRef()

    static contextType = ChartViewContext
    context!: ChartViewContextType

    @observable hoverX?: number
    @action.bound onHover(hoverX: number|undefined) {
        this.hoverX = hoverX
    }

    @computed get chart() { return this.props.chart }
    @computed get bounds() { return this.props.bounds }
    @computed get transform() { return this.props.chart.lineChart }

    // Order of the legend items on a line chart should visually correspond
    // to the order of the lines as the approach the legend
    @computed get legendItems() {
        // If there are any projections, ignore non-projection legends
        // Bit of a hack
        let toShow = this.transform.groupedData
        if (toShow.some(g => !!g.isProjection))
            toShow = this.transform.groupedData.filter(g => g.isProjection)

        return toShow.map(d => {
            const lastValue = (last(d.values) as LineChartValue).y
            const valueStr = this.transform.yAxis.tickFormat(lastValue)

            return {
                color: d.color,
                key: d.key,
                // E.g. https://ourworldindata.org/grapher/size-poverty-gap-world
                label: this.chart.hideLegend ? valueStr : `${valueStr} ${this.chart.data.formatKey(d.key)}`,
                yValue: lastValue
            }
        })
    }

    @computed get legend(): HeightedLegend | undefined {
        const that = this
        return new HeightedLegend({
            get maxWidth() { return that.bounds.width / 3 },
            get fontSize() { return that.chart.baseFontSize },
            get items() { return that.legendItems }<commit_msg>Don't show numbers on end of line chart lines<commit_after>/* LineChart.tsx
 * ================
 *
 * A standard line chart.
 *
 */

import * as React from 'react'
import { last, guid, sortBy } from './Util'
import { computed, action, observable } from 'mobx'
import { observer } from 'mobx-react'
import { select } from 'd3-selection'
import { easeLinear } from 'd3-ease'

import { ChartConfig } from './ChartConfig'
import { Bounds } from './Bounds'
import { AxisBox } from './AxisBox'
import { StandardAxisBoxView } from './StandardAxisBoxView'
import { Lines } from './Lines'
import { HeightedLegend, HeightedLegendView } from './HeightedLegend'
import { ComparisonLine } from './ComparisonLine'
import { Tooltip } from './Tooltip'
import { NoData } from './NoData'
import { formatYear } from './Util'
import { ChartViewContext, ChartViewContextType } from './ChartViewContext'

export interface LineChartValue {
    x: number,
    y: number,
    time: number
}

export interface LineChartSeries {
    key: string,
    color: string,
    values: LineChartValue[],
    classed?: string,
    isProjection?: boolean,
    formatValue: (value: number) => string
}

@observer
export class LineChart extends React.Component<{ bounds: Bounds, chart: ChartConfig }> {
    base: React.RefObject<SVGGElement> = React.createRef()

    static contextType = ChartViewContext
    context!: ChartViewContextType

    @observable hoverX?: number
    @action.bound onHover(hoverX: number|undefined) {
        this.hoverX = hoverX
    }

    @computed get chart() { return this.props.chart }
    @computed get bounds() { return this.props.bounds }
    @computed get transform() { return this.props.chart.lineChart }

    // Order of the legend items on a line chart should visually correspond
    // to the order of the lines as the approach the legend
    @computed get legendItems() {
        // If there are any projections, ignore non-projection legends
        // Bit of a hack
        let toShow = this.transform.groupedData
        if (toShow.some(g => !!g.isProjection))
            toShow = this.transform.groupedData.filter(g => g.isProjection)

        return toShow.map(d => {
            const lastValue = (last(d.values) as LineChartValue).y
            const valueStr = this.transform.yAxis.tickFormat(lastValue)

            return {
                color: d.color,
                key: d.key,
                // E.g. https://ourworldindata.org/grapher/size-poverty-gap-world
                label: this.chart.hideLegend ? "" : `${this.chart.data.formatKey(d.key)}`, //this.chart.hideLegend ? valueStr : `${valueStr} ${this.chart.data.formatKey(d.key)}`,
                yValue: lastValue
            }
        })
    }

    @computed get legend(): HeightedLegend | undefined {
        const that = this
        return new HeightedLegend({
            get maxWidth() { return that.bounds.width / 3 },
            get fontSize() { return that.chart.baseFontSize },
            get items() { return that.legendItems }
{'fraction_non_alphanumeric': 0.11888701517706576, 'fraction_numerical': 0.001011804384485666, 'mean_word_length': 2.758555133079848, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '72399', 'n_tokens_mistral': 1731, 'n_tokens_neox': 1682, 'n_words': 496}