import * as React from 'react'; import { useKnobTheme } from '../core/theme'; import { Meter, type MeterProps } from './Meter'; export interface MeterBridgeChannel { label: string; value: number; /** Peak signal for clip detection, when different from `value`. */ clipValue?: number; } export interface MeterBridgeProps extends Omit { channels: readonly MeterBridgeChannel[]; /** Numeric peak-hold text above each bar. Default: true. */ showPeakText?: boolean; /** Decimals for the peak text. Default: 1. */ peakTextDecimals?: number; /** Block caption under the bridge. */ label?: string; 'aria-label'?: string; } /** * A labeled multi-strip meter block — n channels, per-channel peak text, one * shared clip LED. The composition every mixer rebuilds, prebuilt. */ export const MeterBridge: React.FC = ({ channels, showPeakText = true, peakTextDecimals = 1, label, min = 0, max = 100, breadth = 16, peakHold = 1200, className, style, ...meterProps }) => { const theme = useKnobTheme(); const values = channels.map(c => c.value); const clipValues = channels.map(c => c.clipValue ?? c.value); const chGap = channels.length > 1 ? 3 : 0; // Numeric peak-hold per channel for the text row. const [peaks, setPeaks] = React.useState(values); React.useEffect(() => { if (peakHold === false) return; setPeaks(prev => prev.length === values.length ? values.map((v, i) => Math.max(v, prev[i] ?? -Infinity)) : values, ); const t = setTimeout(() => setPeaks(values), peakHold); return () => clearTimeout(t); // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(values), peakHold]); const cell: React.CSSProperties = { width: breadth, textAlign: 'center', fontFamily: theme.fontMono, whiteSpace: 'nowrap', overflow: 'visible', }; return (
{showPeakText && (
{peaks.map((p, i) => ( {Math.max(p, min) <= min ? '—' : Math.min(p, max).toFixed(peakTextDecimals)} ))}
)}
{channels.map(c => ( {c.label} ))}
{label && ( {label} )}
); };