import { createElement, type SVGProps } from "react"; import { Box } from "../../ui"; import { useThemeColors } from "../../theme/theme-context"; import { compactSegmentLabel, formatGaugeValue, valueToDegrees, type SpeedometerGaugeProps, } from "./model"; const DESKTOP_VIEWBOX_WIDTH = 520; const DESKTOP_VIEWBOX_HEIGHT = 232; const DESKTOP_COMPACT_VIEWBOX_HEIGHT = 214; const DESKTOP_CENTER_X = 260; const DESKTOP_CENTER_Y = 182; const DESKTOP_ARC_RADIUS = 138; const DESKTOP_NEEDLE_RADIUS = 104; function SvgText(props: SVGProps) { return createElement("text", props); } function polarToCartesian(centerX: number, centerY: number, radius: number, angleDegrees: number) { const angleRadians = (angleDegrees - 90) * Math.PI / 180; return { x: centerX + radius * Math.cos(angleRadians), y: centerY + radius * Math.sin(angleRadians), }; } function describeArc(centerX: number, centerY: number, radius: number, startAngle: number, endAngle: number): string { const start = polarToCartesian(centerX, centerY, radius, endAngle); const end = polarToCartesian(centerX, centerY, radius, startAngle); const largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1"; return `M ${start.x.toFixed(2)} ${start.y.toFixed(2)} A ${radius} ${radius} 0 ${largeArcFlag} 0 ${end.x.toFixed(2)} ${end.y.toFixed(2)}`; } export function DesktopSpeedometerGauge({ value, valueLabel, width, segments, min, max, currentLabel, minWidth, maxWidth, compact, }: Required) { const colors = useThemeColors(); const gaugeWidth = Math.min(Math.max(width - 2, minWidth), maxWidth); const gaugeHeight = compact ? 9 : 12; const viewBoxHeight = compact ? DESKTOP_COMPACT_VIEWBOX_HEIGHT : DESKTOP_VIEWBOX_HEIGHT; const needleAngle = valueToDegrees(value, min, max); const needleEnd = polarToCartesian(DESKTOP_CENTER_X, DESKTOP_CENTER_Y, DESKTOP_NEEDLE_RADIUS, needleAngle); return ( {segments.map((segment) => ( = segment.from && value <= segment.to ? 0.96 : 0.42} /> ))} {segments.map((segment) => { const midpoint = (valueToDegrees(segment.from, min, max) + valueToDegrees(segment.to, min, max)) / 2; const point = polarToCartesian(DESKTOP_CENTER_X, DESKTOP_CENTER_Y, DESKTOP_ARC_RADIUS + 36, midpoint); return ( {compactSegmentLabel(segment)} ); })} {[min, min + (max - min) * 0.25, min + (max - min) * 0.5, min + (max - min) * 0.75, max].map((tick) => { const angle = valueToDegrees(tick, min, max); const outer = polarToCartesian(DESKTOP_CENTER_X, DESKTOP_CENTER_Y, DESKTOP_ARC_RADIUS + 16, angle); const inner = polarToCartesian(DESKTOP_CENTER_X, DESKTOP_CENTER_Y, DESKTOP_ARC_RADIUS - 10, angle); return ( ); })} {formatGaugeValue(value)} ); }