sendou.ink/app/components/Chart.tsx
Kalle c8ea75ebb6
SQL solution migration to Kysely + getting rid of routes folder (#1530)
* Kysely initial

* Badges initial

* Badge routes migrated

* Badges migrated

* Calendar work

* Fix one type problem

* Calendar work

* findResultsByUserId work

* Calendar reworking finished

* PlusSuggestions work

* Migrated suggestions

* Builds progress

* Migrated builds

* Admin migrated

* Migrate articles

* User search

* Faster getUser

* Selectable/insertable as global

* Refresh prod db script + patronTier index

* identifierToUserId

* updateProfile

* findByIdentifier

* More indexes

* User upsert

* upsertLite

* findAllPlusMembers

* updateResultHighlights

* updateMany

* User finished migration

* Fix types

* Fix PlusVotingResult typing

* PlusVotingRepository WIP

* Migrated resultsByMonthYear

* Migrated plusVotes (done with db. related migrations)

* Plus code to features folder

* Fix TODOs

* Export

* Fix range

* Migrate some user pages

* Move rest user routes

* Move /play

* Map list generator

* Front page

* Move map list generation logic

* Move plus voting logic

* Info

* API

* Adjust TODOs

* theme

* Auth

* Remove TODO
2023-11-04 13:15:36 +02:00

141 lines
3.5 KiB
TypeScript

import clsx from "clsx";
import * as React from "react";
import { type AxisOptions, Chart as ReactChart } from "react-charts";
import type { TooltipRendererProps } from "react-charts/types/components/TooltipRenderer";
import { useTranslation } from "~/hooks/useTranslation";
import { useIsMounted } from "~/hooks/useIsMounted";
import { Theme, useTheme } from "~/features/theme/core/provider";
export default function Chart({
options,
containerClassName,
headerSuffix,
valueSuffix,
}: {
options: [
{ label: string; data: Array<{ primary: Date; secondary: number }> },
];
containerClassName?: string;
headerSuffix?: string;
valueSuffix?: string;
}) {
const theme = useTheme();
const isMounted = useIsMounted();
const primaryAxis = React.useMemo<
AxisOptions<(typeof options)[number]["data"][number]>
>(
() => ({
getValue: (datum) => datum.primary,
}),
[],
);
const secondaryAxes = React.useMemo<
AxisOptions<(typeof options)[number]["data"][number]>[]
>(
() => [
{
getValue: (datum) => datum.secondary,
},
],
[],
);
if (!isMounted) {
return <div className={clsx("chart__container", containerClassName)} />;
}
return (
<div className={clsx("chart__container", containerClassName)}>
<ReactChart
options={{
data: options,
tooltip: {
render: (props) => (
<ChartTooltip
{...props}
headerSuffix={headerSuffix}
valueSuffix={valueSuffix}
/>
),
},
primaryAxis,
secondaryAxes,
dark: theme.htmlThemeClass === Theme.DARK,
defaultColors: [
"var(--theme)",
"var(--theme-secondary)",
"var(--theme-info)",
],
}}
/>
</div>
);
}
interface ChartTooltipProps extends TooltipRendererProps<any> {
headerSuffix?: string;
valueSuffix?: string;
}
function ChartTooltip({
focusedDatum,
headerSuffix = "",
valueSuffix = "",
}: ChartTooltipProps) {
const { i18n } = useTranslation();
const dataPoints = focusedDatum?.interactiveGroup ?? [];
const header = () => {
const primaryValue = dataPoints[0]?.primaryValue;
if (!primaryValue) return null;
if (primaryValue instanceof Date) {
return primaryValue.toLocaleDateString(i18n.language, {
weekday: "short",
day: "numeric",
month: "long",
});
}
return primaryValue;
};
return (
<div className="chart__tooltip">
<h3 className="text-center text-md">
{header()}
{headerSuffix}
</h3>
{dataPoints.map((dataPoint, index) => {
const color = dataPoint.style?.fill ?? "var(--theme)";
return (
<div key={index} className="stack horizontal items-center sm">
<div
className={clsx("chart__dot", {
chart__dot__focused:
focusedDatum?.seriesId === dataPoint.seriesId,
})}
style={
{
"--dot-color": color,
"--dot-color-outline": color.replace(")", "-transparent)"),
} as any
}
/>
<div className="chart__tooltip__label">
{dataPoint.originalSeries.label}
</div>
<div className="chart__tooltip__value">
{dataPoint.secondaryValue}
{valueSuffix}
</div>
</div>
);
})}
</div>
);
}