openreplay/frontend/app/components/shared/Insights/SankeyChart/SankeyChart.tsx
2022-12-23 11:24:35 +01:00

134 lines
3.8 KiB
TypeScript

import React from 'react';
import { Sankey, Tooltip, Rectangle, Layer, ResponsiveContainer } from 'recharts';
type Node = {
name: string;
}
type Link = {
source: number;
target: number;
value: number;
}
export interface SankeyChartData {
links: Link[];
nodes: Node[];
}
interface Props {
data: SankeyChartData;
nodePadding?: number;
nodeWidth?: number;
}
function SankeyChart(props: Props) {
const { data, nodePadding = 50, nodeWidth = 10 } = props;
return (
<div className="rounded border shadow">
<div className="text-lg p-3 border-b bg-gray-lightest">Sankey Chart</div>
<div className="">
<ResponsiveContainer height={500} width="100%">
<Sankey
width={960}
height={500}
data={data}
// node={{ stroke: '#77c878', strokeWidth: 0 }}
node={<CustomNodeComponent />}
nodePadding={nodePadding}
nodeWidth={nodeWidth}
margin={{
left: 10,
right: 100,
top: 10,
bottom: 10,
}}
link={<CustomLinkComponent />}
>
<defs>
<linearGradient id={'linkGradient'}>
<stop offset="0%" stopColor="rgba(0, 136, 254, 0.5)" />
<stop offset="100%" stopColor="rgba(0, 197, 159, 0.3)" />
</linearGradient>
</defs>
<Tooltip content={<CustomTooltip />} />
</Sankey>
</ResponsiveContainer>
</div>
</div>
);
}
export default SankeyChart;
const CustomTooltip = (props: any) => {
return <div className="rounded bg-white border p-0 px-1 text-sm">test</div>;
// if (active && payload && payload.length) {
// return (
// <div className="custom-tooltip">
// <p className="label">{`${label} : ${payload[0].value}`}</p>
// <p className="intro">{getIntroOfPage(label)}</p>
// <p className="desc">Anything you want can be displayed here.</p>
// </div>
// );
// }
return null;
};
function CustomNodeComponent({ x, y, width, height, index, payload, containerWidth }: any) {
const isOut = x + width + 6 > containerWidth;
return (
<Layer key={`CustomNode${index}`}>
<Rectangle x={x} y={y} width={width} height={height} fill="#5192ca" fillOpacity="1" />
<text
textAnchor={isOut ? 'end' : 'start'}
x={isOut ? x - 6 : x + width + 6}
y={y + height / 2}
fontSize="8"
// stroke="#333"
>
{payload.name}
</text>
<text
textAnchor={isOut ? 'end' : 'start'}
x={isOut ? x - 6 : x + width + 6}
y={y + height / 2 + 13}
fontSize="12"
// stroke="#333"
// strokeOpacity="0.5"
>
{payload.value + 'k'}
</text>
</Layer>
);
}
const CustomLinkComponent = (props: any) => {
const [fill, setFill] = React.useState('url(#linkGradient)');
const { sourceX, targetX, sourceY, targetY, sourceControlX, targetControlX, linkWidth, index } =
props;
return (
<Layer key={`CustomLink${index}`}>
<path
d={`
M${sourceX},${sourceY + linkWidth / 2}
C${sourceControlX},${sourceY + linkWidth / 2}
${targetControlX},${targetY + linkWidth / 2}
${targetX},${targetY + linkWidth / 2}
L${targetX},${targetY - linkWidth / 2}
C${targetControlX},${targetY - linkWidth / 2}
${sourceControlX},${sourceY - linkWidth / 2}
${sourceX},${sourceY - linkWidth / 2}
Z
`}
fill={fill}
strokeWidth="0"
onMouseEnter={() => {
setFill('rgba(0, 136, 254, 0.5)');
}}
onMouseLeave={() => {
setFill('url(#linkGradient)');
}}
/>
</Layer>
);
};