diff --git a/components/charts/chart-tooltip.tsx b/components/charts/chart-tooltip.tsx new file mode 100644 index 0000000..c31d1b8 --- /dev/null +++ b/components/charts/chart-tooltip.tsx @@ -0,0 +1,49 @@ +import type { ReactNode } from "react"; + +interface ChartTooltipPayload { + color?: string; + dataKey?: string | number; + name?: ReactNode; + payload?: Record; + value?: unknown; +} + +interface ChartTooltipProps { + active?: boolean; + label?: ReactNode; + labelFormatter?: (label: ReactNode) => ReactNode; + payload?: ChartTooltipPayload[]; + valueFormatter?: (entry: ChartTooltipPayload) => ReactNode; +} + +export function ChartTooltip({ active, label, labelFormatter, payload, valueFormatter }: ChartTooltipProps) { + if (!active || !payload?.length) return null; + + return ( +
+ {label !== undefined && ( +

+ {labelFormatter ? labelFormatter(label) : label} +

+ )} +
+ {payload.map((entry, index) => ( +
+ + {entry.color && ( + + + {valueFormatter ? valueFormatter(entry) : String(entry.value ?? "—")} + +
+ ))} +
+
+ ); +} diff --git a/components/charts/day-forecast-charts.tsx b/components/charts/day-forecast-charts.tsx index c582458..ff8579e 100644 --- a/components/charts/day-forecast-charts.tsx +++ b/components/charts/day-forecast-charts.tsx @@ -2,6 +2,7 @@ import { Bar, CartesianGrid, ComposedChart, Legend, Line, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; import { Card } from "@/components/ui/card"; +import { ChartTooltip } from "@/components/charts/chart-tooltip"; import { CHART_COLORS } from "@/lib/chart-theme"; import { useI18n } from "@/lib/i18n"; import { formatForecastRainfall, formatForecastTemperature } from "@/lib/forecast-utils"; @@ -56,17 +57,16 @@ export function DayForecastCharts({ hours }: { hours: HourlyForecast[] }) { unit={temperatureUnitLabel} /> [ - typeof value === "number" - ? formatTemperatureValue(value, language, temperatureUnit) - : formatForecastTemperature(null, language, temperatureUnit), - ]} + cursor={{ stroke: CHART_COLORS.grid, strokeDasharray: "4 4" }} + content={ + + typeof entry.value === "number" + ? formatTemperatureValue(entry.value, language, temperatureUnit) + : formatForecastTemperature(null, language, temperatureUnit) + } + /> + } /> [ - name === t("forecast.precipitation") - ? formatForecastRainfall(typeof value === "number" ? value : null, language) - : `${typeof value === "number" ? value : "—"}%`, - name, - ]} + cursor={{ fill: "hsl(var(--border) / 0.18)" }} + content={ + + entry.dataKey === "precipitation" + ? formatForecastRainfall(typeof entry.value === "number" ? entry.value : null, language) + : `${typeof entry.value === "number" ? entry.value : "—"}%` + } + /> + } /> [`${item.payload.value} ${item.payload.unit}`, item.payload.name]} + content={ + { + const row = entry.payload; + return `${row?.value ?? "—"} ${row?.unit ?? ""}`.trim(); + }} + /> + } /> {rows.map((row) => (