Files
design-system/src/design-system/composites/LineChart/LineChart.tsx
hsiegeln b0bd9a4ce2 feat: add LineChart, AreaChart, BarChart wrapper components
Wrap ThemedChart with convenient series-based API that transforms
ChartSeries[] into the flat record format ThemedChart expects.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:12:02 +02:00

102 lines
2.3 KiB
TypeScript

import { useMemo } from 'react'
import { Line, ReferenceLine } from 'recharts'
import { ThemedChart } from '../ThemedChart/ThemedChart'
import { CHART_COLORS } from '../../utils/rechartsTheme'
export interface DataPoint {
x: any
y: number
}
export interface ChartSeries {
label: string
data: DataPoint[]
color?: string
}
interface LineChartProps {
series: ChartSeries[]
height?: number
width?: number
yLabel?: string
xLabel?: string
threshold?: { value: number; label: string }
className?: string
}
function formatTime(d: Date): string {
const h = String(d.getHours()).padStart(2, '0')
const m = String(d.getMinutes()).padStart(2, '0')
return `${h}:${m}`
}
export function LineChart({
series,
height,
width,
yLabel,
xLabel,
threshold,
className,
}: LineChartProps) {
const { data, hasDateX } = useMemo(() => {
const map = new Map<string, Record<string, any>>()
let dateDetected = false
for (const s of series) {
for (const pt of s.data) {
const isDate = pt.x instanceof Date
if (isDate) dateDetected = true
const key = isDate ? pt.x.getTime().toString() : String(pt.x)
if (!map.has(key)) {
map.set(key, { _x: isDate ? formatTime(pt.x) : pt.x })
}
map.get(key)![s.label] = pt.y
}
}
return { data: Array.from(map.values()), hasDateX: dateDetected }
}, [series])
const chart = (
<ThemedChart
data={data}
height={height}
xDataKey="_x"
xType={hasDateX ? 'category' : 'category'}
xTickFormatter={hasDateX ? (v: any) => String(v) : undefined}
yLabel={yLabel}
className={className}
>
{series.map((s, i) => (
<Line
key={s.label}
type="monotone"
dataKey={s.label}
stroke={s.color ?? CHART_COLORS[i % CHART_COLORS.length]}
dot={false}
strokeWidth={1.5}
/>
))}
{threshold && (
<ReferenceLine
y={threshold.value}
stroke="var(--text-muted)"
strokeDasharray="4 4"
label={{
value: threshold.label,
position: 'insideTopRight',
style: { fontSize: 10, fill: 'var(--text-muted)' },
}}
/>
)}
</ThemedChart>
)
if (width) {
return <div style={{ width }}>{chart}</div>
}
return chart
}