import { h } from 'preact'; import { useState, useEffect } from 'preact/hooks'; import { getSignalStore, getMetaStore } from '../lib/stores'; import type { Widget, SignalValue, SignalMeta } from '../lib/types'; const DEFAULT_VALUE: SignalValue = { value: null, quality: 'unknown', ts: null }; function qualityColor(q: string): string { switch (q) { case 'good': return '#4ade80'; case 'uncertain': return '#fbbf24'; case 'bad': return '#f87171'; default: return '#6b7280'; } } interface Props { widget: Widget; onContextMenu?: (e: MouseEvent) => void; } export default function BarV({ widget, onContextMenu }: Props) { const sigRef = widget.signals[0]; const [sv, setSv] = useState(DEFAULT_VALUE); const [meta, setMeta] = useState(null); useEffect(() => { if (!sigRef) return; const unsubV = getSignalStore(sigRef).subscribe(setSv); const unsubM = getMetaStore(sigRef).subscribe(setMeta); return () => { unsubV(); unsubM(); }; }, [sigRef?.ds, sigRef?.name]); const label = widget.options['label'] ?? sigRef?.name ?? ''; const unit = widget.options['unit'] ?? meta?.unit ?? ''; const minVal = parseFloat(widget.options['min'] ?? String(meta?.displayLow ?? 0)); const maxVal = parseFloat(widget.options['max'] ?? String(meta?.displayHigh ?? 100)); const quality = sv.quality; const rawV = sv.value; const rawValue: number | null = rawV === null || rawV === undefined ? null : typeof rawV === 'number' ? rawV : parseFloat(String(rawV)); function displayValue(): string { if (rawValue === null || isNaN(rawValue)) return '---'; return Number.isFinite(rawValue) ? rawValue.toPrecision(4).replace(/\.?0+$/, '') : String(rawValue); } function fillPercent(): number { if (rawValue === null || isNaN(rawValue)) return 0; const frac = maxVal === minVal ? 0 : (rawValue - minVal) / (maxVal - minVal); return Math.max(0, Math.min(100, frac * 100)); } return (
{label &&
{label}
}
{displayValue()} {unit && {unit}}
); }