indiespirit / react-native-chart-kit

📊React Native Chart Kit: Line Chart, Bezier Line Chart, Progress Ring, Bar chart, Pie chart, Contribution graph (heatmap)
https://expo.io/@indiespirit/react-native-chart-kit
MIT License
2.88k stars 662 forks source link

How to use decorator for line chart - (show a tooltip onDataPointClick) #123

Open luatnd opened 5 years ago

luatnd commented 5 years ago

Hi I took a look at the /src/line-chart.js . And I can see it support decorator, but I don't know how to use it to make a tooltip when I click a data point. Is there anyone can show me an example ?

fabOnReact commented 5 years ago

@luatnd I also need to create the tooltip as the forecast X axis labels disappears depending on the chart hight

luatnd commented 5 years ago

@fabriziobertoglio1987 I need to clone this repository to my local machine, then modify the code manually :D Unfortunately, I modify too much to gain my project's requirement - so I cannot contribute back. Below is how I make the tooltip:

image

image

RPReplay_Final1566529155 2019-08-23 10_24_08

shariqahmed525 commented 5 years ago

@luatnd Can you please share me the code of tooltip or how can I render it on the dot?

luatnd commented 5 years ago

@shariqahmed1 I upload to my fork repos, be aware that I've modified it to much so it's might different from this origin repo. https://github.com/luatnd/react-native-chart-kit

Here is how I use it:

const lineChartData = {"labels":[],"datasets":[{"data":[13.8,13.8,13.8,13.8,13.8,13.8,13,13.8,13.8,13.8,13.8],"strokeWidth":1,"labels":[1566201802,1566288253,1566374595,1566460963,1566547429,1566806673,1566892961,1566892961,1566979498,1567065874,1567152215]}]};
<LineChart
            ref={this.refLine}
            data={lineChartData}
            withDots={true}
            withHiddenDots={true}
            withShadow={true}
            width={Dimensions.get('window').width - 10} // from react-native
            height={175}
            yAxisLabel={''}
            yAxisLabelVisible={false}
            chartConfig={chartConfig}
            bezier
            withInnerLines={false}
            withOuterLines={false}
            style={s.lineChart}
            getTooltipTextX={this.getTooltipTextXLine}
            getTooltipTextY={this.getTooltipTextYLine}
            onChartClick={this.onLineChartPressIn}
            // onRendered={this.onRenderedLine}
          />

 // Tooltip: show date and stock price
  getTooltipTextXLine = (i, v, dataset) => moment(dataset.labels[i] * 1000).format('ddd, DD/MM, HH:mm:ss');
  getTooltipTextYLine = (i, v, dataset) => I18n.t('stock.price') + ': ' + v;

  @boundMethod // use external npm package to autobind this
  onLineChartPressIn(index, x,y) {
    if (this.BarChart) {
      const firstDataset = this.state.barChartData.datasets[0];
      this.BarChart.handleChartClickByIndex(index, x, y, firstDataset); // Simulate the clicked event so that bar chart can show tooltip when we click on LineChart
    }
  }

Charts was modified to handle and show tooltip internally. You can read my latest commit, sorry about a bunch of code in only 1 commit ^^

shariqahmed525 commented 5 years ago

@luatnd Thank you so much I try this

vsalvans commented 5 years ago

Here is an example of type of HOC to add tooltips using LineChart component. It's inspired on the @luatnd solution.

import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { Circle, G, Rect, Text } from 'react-native-svg';
import { Dimensions } from 'react-native';
import { LineChart } from 'react-native-chart-kit';

const screenWidth = Dimensions.get('window').width;

const Tooltip = ({ x, y, textX, textY, stroke, pointStroke, position }) => {
    let tipW = 136,
        tipH = 36,
        tipX = 5,
        tipY = -9,
        tipTxtX = 12,
        tipTxtY = 6;
    const posY = y;
    const posX = x;

    if (posX > screenWidth - tipW) {
        tipX = -(tipX + tipW);
        tipTxtX = tipTxtX - tipW - 6;
    }

    const boxPosX = position === 'left' ? posX - tipW - 10 : posX;

    return (
        <G>
            <Circle
                cx={posX}
                cy={posY}
                r={4}
                stroke={pointStroke}
                strokeWidth={2}
                fill={'blue'}
            />
            <G x={boxPosX < 40 ? 40 : boxPosX} y={posY}>
                <Rect
                    x={tipX + 1}
                    y={tipY - 1}
                    width={tipW - 2}
                    height={tipH - 2}
                    fill={'rgba(255, 255, 255, 0.9)'}
                    rx={2}
                    ry={2}
                />
                <Rect
                    x={tipX}
                    y={tipY}
                    width={tipW}
                    height={tipH}
                    rx={2}
                    ry={2}
                    fill={'transparent'}
                    stroke={stroke}
                />
                <Text x={tipTxtX} y={tipTxtY} fontSize="10" textAnchor="start">
                    {textX}
                </Text>

                <Text
                    x={tipTxtX}
                    y={tipTxtY + 14}
                    fontSize="12"
                    textAnchor="start">
                    {textY}
                </Text>
            </G>
        </G>
    );
};

Tooltip.propTypes = {
    x: PropTypes.func.isRequired,
    y: PropTypes.func.isRequired,
    height: PropTypes.number,
    stroke: PropTypes.string,
    pointStroke: PropTypes.string,
    textX: PropTypes.string,
    textY: PropTypes.string,
    position: PropTypes.string,
};

Tooltip.defaultProps = {
    position: 'rigth',
};

const tooltipDecorators = (state, data, valueFormatter) => () => {
    if (state === null) {
        return null;
    }

    const { index, value, x, y } = state;
    const textX = data.labels[index];
    console.log(data.labels);
    const position = data.labels.length === index + 1 ? 'left' : 'right';

    return (
        <Tooltip
            textX={String(textX)}
            textY={valueFormatter(value)}
            x={x}
            y={y}
            stroke={'#00ccff'}
            pointStroke={'#00ccff'}
            position={position}
        />
    );
};

const LineChartWithTooltips = ({ valueFormatter, ...props }) => {
    const [state, setState] = useState(null);
    return (
        <LineChart
            {...props}
            decorator={tooltipDecorators(state, props.data, valueFormatter)}
            onDataPointClick={setState}
        />
    );
};

LineChartWithTooltips.propTypes = {
    valueFormatter: PropTypes.func,
};

LineChartWithTooltips.defaultProps = {
    valueFormatter: value => String(value),
};

export default LineChartWithTooltips;
mohit-kaushik commented 4 years ago

Hi.. I wrote one in detail check this https://medium.com/@mohitkaushik2468/adding-tooltip-to-react-native-charts-67606c5d3182

nzrin commented 2 years ago

Here is an example of type of HOC to add tooltips using LineChart component. It's inspired on the @luatnd solution.

import PropTypes from 'prop-types';
import React, { useState } from 'react';
import { Circle, G, Rect, Text } from 'react-native-svg';
import { Dimensions } from 'react-native';
import { LineChart } from 'react-native-chart-kit';

const screenWidth = Dimensions.get('window').width;

const Tooltip = ({ x, y, textX, textY, stroke, pointStroke, position }) => {
    let tipW = 136,
        tipH = 36,
        tipX = 5,
        tipY = -9,
        tipTxtX = 12,
        tipTxtY = 6;
    const posY = y;
    const posX = x;

    if (posX > screenWidth - tipW) {
        tipX = -(tipX + tipW);
        tipTxtX = tipTxtX - tipW - 6;
    }

    const boxPosX = position === 'left' ? posX - tipW - 10 : posX;

    return (
        <G>
            <Circle
                cx={posX}
                cy={posY}
                r={4}
                stroke={pointStroke}
                strokeWidth={2}
                fill={'blue'}
            />
            <G x={boxPosX < 40 ? 40 : boxPosX} y={posY}>
                <Rect
                    x={tipX + 1}
                    y={tipY - 1}
                    width={tipW - 2}
                    height={tipH - 2}
                    fill={'rgba(255, 255, 255, 0.9)'}
                    rx={2}
                    ry={2}
                />
                <Rect
                    x={tipX}
                    y={tipY}
                    width={tipW}
                    height={tipH}
                    rx={2}
                    ry={2}
                    fill={'transparent'}
                    stroke={stroke}
                />
                <Text x={tipTxtX} y={tipTxtY} fontSize="10" textAnchor="start">
                    {textX}
                </Text>

                <Text
                    x={tipTxtX}
                    y={tipTxtY + 14}
                    fontSize="12"
                    textAnchor="start">
                    {textY}
                </Text>
            </G>
        </G>
    );
};

Tooltip.propTypes = {
    x: PropTypes.func.isRequired,
    y: PropTypes.func.isRequired,
    height: PropTypes.number,
    stroke: PropTypes.string,
    pointStroke: PropTypes.string,
    textX: PropTypes.string,
    textY: PropTypes.string,
    position: PropTypes.string,
};

Tooltip.defaultProps = {
    position: 'rigth',
};

const tooltipDecorators = (state, data, valueFormatter) => () => {
    if (state === null) {
        return null;
    }

    const { index, value, x, y } = state;
    const textX = data.labels[index];
    console.log(data.labels);
    const position = data.labels.length === index + 1 ? 'left' : 'right';

    return (
        <Tooltip
            textX={String(textX)}
            textY={valueFormatter(value)}
            x={x}
            y={y}
            stroke={'#00ccff'}
            pointStroke={'#00ccff'}
            position={position}
        />
    );
};

const LineChartWithTooltips = ({ valueFormatter, ...props }) => {
    const [state, setState] = useState(null);
    return (
        <LineChart
            {...props}
            decorator={tooltipDecorators(state, props.data, valueFormatter)}
            onDataPointClick={setState}
        />
    );
};

LineChartWithTooltips.propTypes = {
    valueFormatter: PropTypes.func,
};

LineChartWithTooltips.defaultProps = {
    valueFormatter: value => String(value),
};

export default LineChartWithTooltips;

Can you provide this solution using Component instead of Hooks? Thanks. @vsalvans