Skip to content

Instantly share code, notes, and snippets.

@Muzietto
Last active September 29, 2022 14:53
Show Gist options
  • Save Muzietto/270dba9ba818edab44d95cab99da0365 to your computer and use it in GitHub Desktop.
Save Muzietto/270dba9ba818edab44d95cab99da0365 to your computer and use it in GitHub Desktop.
Recharts Tooltip: how to show only the value of ONE data point inside a tooltip
import React from 'react';
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
ReferenceLine,
Tooltip,
} from 'recharts';
import {
CustomTooltip,
CustomizedTick,
} from '....';
import useDimensions from 'react-use-dimensions';
import { tooltipCollector } from '....';
const ChartUsingCustomTooltip = ({
data = [], // points
series = [], // dataSeries
chartValuesUnits = 'integers',
xAxis = {},
yAxis = {},
}) => {
const THE_COLLECTOR = tooltipCollector();
const [chartRef, { width }] = useDimensions(); // [chartRef, { x, y, width, height }]
return <div ref={chartRef}>
<LineChart
width={width * 0.94}
height={600}
data={data}
margin={{
top: 50, right: 5, left: 5, bottom: 35,
}}
>
<CartesianGrid strokeDasharray='3 3' vertical={false} />
<XAxis
{...xAxis}
padding={{ left: 30, right: 30 }}
axisLine={false}
tickLine={false}
xAxisId={0}
minTickGap={8}
/>
<YAxis
{...yAxis}
axisLine={false}
tickLine={false}
tick={<CustomizedTick THE_COLLECTOR={THE_COLLECTOR} />}
/>
<Tooltip content={<CustomTooltip
THE_COLLECTOR={THE_COLLECTOR}
unit={chartValuesUnits}
/>} />
{series.map((s, index) =>
<Line
key={`rlc_${index}_${s.dataKey}_series`}
type='linear'
dataKey={s.dataKey}
stroke={s.color}
strokeWidth={3}
dot={{ r: 6 }}
/>)}
</LineChart>
</div>;
};
function CustomizedTick(props) {
const { x, y, payload, THE_COLLECTOR } = props;
THE_COLLECTOR.collect(payload.value, y);
return (
<g>
<text x={x} y={y} fill='#5d6571' textAnchor='end' dy={16}>{payload.value}</text>
</g>
);
}
function CustomTooltip({
active,
payload,
unit,
label,
coordinate,
THE_COLLECTOR
}) {
if (payload === null) return null;
if (active) {
const { min, max } = THE_COLLECTOR.maxAndMin();
const threshold = min.value / 30;
const deltaY = max.y - min.y;
const deltaValue = max.value - min.value;
const cursorValue = min.value - deltaValue * ((min.y - coordinate.y) / deltaY);
const points = payload.map(p => {
const { color, stroke, dataKey, fill, name, payload } = p;
return {
color,
stroke,
dataKey,
fill,
name: _t(name),
value: payload[dataKey],
};
});
const nearestPointIndexes = points.reduce((acc, curr, index) => {
const deltaValue = Math.abs(curr.value - cursorValue);
if (acc.length === 0) return (deltaValue < threshold) ? [{ index, deltaValue }] : [];
if (Math.abs(deltaValue - acc[0].deltaValue) < threshold) return acc.concat([{ index, deltaValue }]);
return acc;
}, []);
if (nearestPointIndexes.length === 0) return null;
const nearestPoints = nearestPointIndexes
.map(({ index }) => points[index]);
return <div>
<p>{label}</p>
{nearestPoints.map((nearestPoint, index) =>
<div key={`nearestPoint_${index}`} >
<p>
{`${nearestPoint.name}: ${tooltipValueFormatter(nearestPoint.value, unit)}`}
</p>
</div>
)};
</div>
}
return null;
}
function tooltipCollector() {
const _collection = [];
let _min = { y: 0, value: 0 };
let _max = { y: 1, value: 1 };
function _setMaxAndMin() {
const ys = _collection.map(obj => obj.y);
const maxY = Math.max(...ys);
const maxYIndex = ys.indexOf(maxY);
_max = _collection[maxYIndex];
const minY = Math.min(...ys);
const minYIndex = ys.indexOf(minY);
_min = _collection[minYIndex];
}
return {
collect: (value, y) => {
_collection.push({ value, y });
_setMaxAndMin(_collection);
},
maxAndMin: () => {
return {
max: { ..._max },
min: { ..._min },
};
},
};
}
@Muzietto
Copy link
Author

Muzietto commented Jun 1, 2021

This gist is explained in this Stack Overflow answer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment