104 lines
3.4 KiB
TypeScript
104 lines
3.4 KiB
TypeScript
import { FC, useEffect, useState } from "react";
|
|
import { PlayerRanking } from "./types";
|
|
import { useSession } from "./Session";
|
|
|
|
interface RaceChartProps {
|
|
playerRanks: PlayerRanking[];
|
|
std: boolean;
|
|
}
|
|
|
|
const determineNiceWidth = (width: number) => {
|
|
const max = 1080;
|
|
if (width >= max) return max;
|
|
else if (width > 768) return width * 0.8;
|
|
else return width * 0.96;
|
|
};
|
|
|
|
const RaceChart: FC<RaceChartProps> = ({ playerRanks, std }) => {
|
|
const [width, setWidth] = useState(determineNiceWidth(window.innerWidth));
|
|
const height = (playerRanks.length + 1) * 40;
|
|
const { players } = useSession();
|
|
|
|
useEffect(() => {
|
|
const handleResize = () => {
|
|
setWidth(determineNiceWidth(window.innerWidth));
|
|
};
|
|
window.addEventListener("resize", handleResize);
|
|
return () => {
|
|
window.removeEventListener("resize", handleResize);
|
|
};
|
|
}, []);
|
|
const padding = 24;
|
|
const gap = 8;
|
|
const maxValue = Math.max(...playerRanks.map((player) => player.rank)) + 1;
|
|
const barHeight = (height - 2 * padding) / playerRanks.length;
|
|
const fontSize = Math.min(barHeight - 1.5 * gap, width / 22);
|
|
|
|
return (
|
|
<svg width={width} height={height} id="RaceChartSVG">
|
|
{playerRanks.map((playerRank, index) => (
|
|
<rect
|
|
key={String(index)}
|
|
x={4}
|
|
y={index * barHeight + padding}
|
|
width={(1 - playerRank.rank / maxValue) * width}
|
|
height={barHeight - gap} // subtract 2 for some spacing between bars
|
|
fill="#36c"
|
|
stroke="aliceblue"
|
|
strokeWidth={4}
|
|
paintOrder={"stroke fill"}
|
|
/>
|
|
))}
|
|
|
|
{playerRanks.map((playerRank, index) => {
|
|
const player = players!.find((p) => p.id === playerRank.p_id);
|
|
return (
|
|
<g key={"group" + index}>
|
|
<text
|
|
key={index + "_name"}
|
|
x={8}
|
|
y={index * barHeight + barHeight / 2 + padding + gap / 2}
|
|
width={(1 - playerRank.rank / maxValue) * width}
|
|
height={barHeight - 8} // subtract 2 for some spacing between bars
|
|
fontSize={fontSize}
|
|
fill="aliceblue"
|
|
stroke="#36c"
|
|
strokeWidth={4}
|
|
fontWeight={"bold"}
|
|
paintOrder={"stroke fill"}
|
|
fontFamily="monospace"
|
|
style={{ whiteSpace: "pre" }}
|
|
>
|
|
{`${String(index + 1).padStart(2)}. ${player?.display_name}`}
|
|
</text>
|
|
<text
|
|
key={index + "_value"}
|
|
x={
|
|
8 +
|
|
(4 +
|
|
Math.max(...players!.map((p, _) => p.display_name.length))) *
|
|
fontSize *
|
|
0.66
|
|
}
|
|
y={index * barHeight + barHeight / 2 + padding + gap / 2}
|
|
width={(1 - playerRank.rank / maxValue) * width}
|
|
height={barHeight - 8} // subtract 2 for some spacing between bars
|
|
fontSize={0.8 * fontSize}
|
|
fill="aliceblue"
|
|
stroke="#36c"
|
|
fontWeight={"bold"}
|
|
fontFamily="monospace"
|
|
strokeWidth={4}
|
|
paintOrder={"stroke fill"}
|
|
style={{ whiteSpace: "pre" }}
|
|
>
|
|
{`${String(playerRank.rank).padStart(5)} ± ${playerRank.std} N = ${playerRank.n}`}
|
|
</text>
|
|
</g>
|
|
);
|
|
})}
|
|
</svg>
|
|
);
|
|
};
|
|
export default RaceChart;
|