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 "react-i18next"; import { Theme, useTheme } from "~/features/theme/core/provider"; import { useIsMounted } from "~/hooks/useIsMounted"; import { useTimeFormat } from "~/hooks/useTimeFormat"; export default function Chart({ options, containerClassName, headerSuffix, valueSuffix, xAxis, }: { options: [ { label: string; data: Array<{ primary: Date; secondary: number }> }, ]; containerClassName?: string; headerSuffix?: string; valueSuffix?: string; xAxis: "linear" | "localTime"; }) { const { i18n } = useTranslation(); const theme = useTheme(); const isMounted = useIsMounted(); const primaryAxis = React.useMemo< AxisOptions<(typeof options)[number]["data"][number]> >( // @ts-expect-error - some weirdness here but maybe not worth fixing as the whole library needs to be replaced (it is unmaintained/deprecated) () => ({ getValue: (datum) => datum.primary, scaleType: xAxis, shouldNice: false, formatters: { scale: (val: any) => { if (val instanceof Date) { return val.toLocaleDateString(i18n.language, { day: "numeric", month: "numeric", }); } return val; }, }, }), [i18n.language, xAxis], ); const secondaryAxes = React.useMemo< AxisOptions<(typeof options)[number]["data"][number]>[] >( () => [ { getValue: (datum) => datum.secondary, }, ], [], ); if (!isMounted) { return
; } return (
( ), }, primaryCursor: false, secondaryCursor: false, primaryAxis, secondaryAxes, dark: theme.htmlThemeClass === Theme.DARK, defaultColors: [ "var(--theme)", "var(--theme-secondary)", "var(--theme-info)", ], }} />
); } interface ChartTooltipProps extends TooltipRendererProps { headerSuffix?: string; valueSuffix?: string; } function ChartTooltip({ focusedDatum, headerSuffix = "", valueSuffix = "", }: ChartTooltipProps) { const { formatDate } = useTimeFormat(); const dataPoints = focusedDatum?.interactiveGroup ?? []; const header = () => { const primaryValue = dataPoints[0]?.primaryValue; if (!primaryValue) return null; if (primaryValue instanceof Date) { return formatDate(primaryValue, { weekday: "short", day: "numeric", month: "long", }); } return primaryValue; }; return (

{header()} {headerSuffix}

{dataPoints.map((dataPoint, index) => { const color = dataPoint.style?.fill ?? "var(--theme)"; return (
{dataPoint.originalSeries.label}
{dataPoint.secondaryValue} {valueSuffix}
); })}
); }