Files
explore/ui/shared/chart/ChartLine.tsx
T
tom goriunovandGitHub e9aa8a3c2f Stats: give emphasis to incomplete data days (#2085)
* draw dotted line for incomplete data

* add title to tooltip

* refactor parts into separate components

* move utils

* refactor render logic of tooltip

* more refactoring

* fix issue in safari

* tweaks

* test

* make an approximation as a straight line.

* update screenshots

* fix the tooltip hiding behavior.

* add prop for disabling animation
2024-07-22 14:41:18 +02:00

75 lines
2.1 KiB
TypeScript

import * as d3 from 'd3';
import React from 'react';
import type { TimeChartItem } from 'ui/shared/chart/types';
import type { AnimationType } from './utils/animations';
import { ANIMATIONS } from './utils/animations';
import { getIncompleteDataLineSource } from './utils/formatters';
interface Props extends React.SVGProps<SVGPathElement> {
xScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
yScale: d3.ScaleTime<number, number> | d3.ScaleLinear<number, number>;
data: Array<TimeChartItem>;
animation: AnimationType;
}
const ChartLine = ({ xScale, yScale, data, animation, ...props }: Props) => {
const dataPathRef = React.useRef<SVGPathElement>(null);
const incompleteDataPathRef = React.useRef<SVGPathElement>(null);
React.useEffect(() => {
const animationFn = ANIMATIONS[animation];
const timeoutId = window.setTimeout(() => {
dataPathRef.current && animationFn(dataPathRef.current);
incompleteDataPathRef.current && animationFn(incompleteDataPathRef.current);
}, 100);
return () => {
window.clearTimeout(timeoutId);
};
}, [ animation ]);
// Recalculate line length if scale has changed
React.useEffect(() => {
if (animation === 'left') {
const totalLength = dataPathRef.current?.getTotalLength();
d3.select(dataPathRef.current).attr(
'stroke-dasharray',
`${ totalLength },${ totalLength }`,
);
}
}, [ xScale, yScale, animation ]);
const line = d3.line<TimeChartItem>()
.x((d) => xScale(d.date))
.y((d) => yScale(d.value))
.curve(d3.curveMonotoneX);
return (
<>
<path
ref={ incompleteDataPathRef }
d={ line(getIncompleteDataLineSource(data)) || undefined }
strokeWidth={ 1 }
strokeLinecap="round"
fill="none"
strokeDasharray="6 6"
opacity={ 0 }
{ ...props }
/>
<path
ref={ dataPathRef }
d={ line(data.filter(({ isApproximate }) => !isApproximate)) || undefined }
strokeWidth={ 1 }
strokeLinecap="round"
fill="none"
opacity={ 0 }
{ ...props }
/>
</>
);
};
export default React.memo(ChartLine);