"use client"; import { AreaChart } from "@mantine/charts"; import { Box, Group, Paper, Stack, Text } from "@mantine/core"; import type { ThroughputPoint } from "./throughput-history"; /** * Series colours. * * Not copied from the `systemResources` network chart, which pairs `orange.5` * with `yellow.5`: that pair scores a normal-vision ΔE of 12.1, below the * legibility floor of 15, so even readers with full colour vision struggle to * tell the two lines apart. This pair was validated instead, and passes every * check — lightness band, chroma, colour-vision-deficiency separation, * normal-vision separation and surface contrast — against BOTH the light and * the dark chart surface, so one pair serves both themes. */ const DOWN_COLOR = "blue.7"; const UP_COLOR = "orange.8"; /** * A floor, not a height: the chart now takes the height the card has left over. * `ResponsiveContainer` measures its parent and renders nothing at all when that * parent collapses to zero, so the wrapper keeps a small non-zero minimum — on a * very short tile the area stays legible instead of disappearing. */ const CHART_MIN_HEIGHT = 32; /** * A dot carries the series identity so the figure beside it can stay in muted * ink: colour is never the only thing distinguishing up from down — the arrow * glyph and the label do it too. */ const SeriesKey = ({ color, label }: { color: string; label: string }) => ( ● {label} ); /** * Defined at module level rather than inline in the `content` prop: recharts * clones this element and injects `payload`, so it keeps a stable identity * across renders. The repo's other chart tooltips pass a closure instead and * each carries a `no-unstable-nested-components` warning for it. */ const ThroughputTooltip = ({ payload, formatRate, upName, downName, }: { payload?: { name?: string | number; value?: string | number }[]; formatRate: (bitsPerSecond: number) => string; upName: string; downName: string; }) => { if (!payload || payload.length === 0) return null; return ( {payload.map((item) => ( {item.name === "up" ? `↑ ${upName}` : `↓ ${downName}`} {formatRate(Number(item.value))} ))} ); }; export interface ThroughputLegendProps { /** Rendered beside the up marker, already formatted. */ upLabel: string; /** Rendered beside the down marker, already formatted. */ downLabel: string; } /** * Sits on the card's "Internet" header row rather than above the graph, so the * figures read as a heading and detach from the area they summarise. Exported * from here instead of being rewritten there: the dots take the very constants * the chart's series use, so the two can never drift apart. */ export const ThroughputLegend = ({ upLabel, downLabel }: ThroughputLegendProps) => ( ); export interface ThroughputChartProps { history: ThroughputPoint[]; /** Formats a raw bits-per-second value for the tooltip. */ formatRate: (bitsPerSecond: number) => string; upName: string; downName: string; } /** * The wrapper is the flex child that claims the card's leftover height, and the * chart fills it with `h="100%"`. Mantine turns that into an inline * `height: 100%` on the chart root, which recharts' `ResponsiveContainer` then * measures; a percentage resolves here because flex layout gives this wrapper a * definite used height, the card above it being a flex item of a `h="100%"` * stack inside a tile of fixed height. */ export const ThroughputChart = ({ history, formatRate, upName, downName }: ThroughputChartProps) => ( , }} /> );