feat(elecraft): the mouse wheel moves the console's sliders

Dragging a four-pixel slider to change the power by five watts is a fussy
gesture, and the Flex and Yaesu consoles have had the wheel for a while —
an operator moving between them expects it.

React registers onWheel as PASSIVE, so preventDefault() there is ignored
and the panel scrolls under the pointer while the value changes; the
listener is attached natively instead, with the live values read through
refs. Both existing panels solved it the same way, so this is a third
copy turned into one shared component -- used by the new console only.
The other two are left alone deliberately: they work, they are in daily
use, and their styling differs in ways a merge would have to guess at.

Power steps 5 W a notch, matching the K3's own coarse step.
This commit is contained in:
2026-08-24 19:25:28 +02:00
parent a27fa08e4e
commit 6b0fc48389
3 changed files with 79 additions and 20 deletions
+13 -18
View File
@@ -11,6 +11,7 @@ import { cn } from '@/lib/utils';
import { useI18n } from '@/lib/i18n';
import { sMeterRST } from '@/lib/rst';
import { MeterBar } from '@/components/MeterBar';
import { WheelRange } from '@/components/WheelRange';
type KenwoodState = {
available: boolean; model?: string; elecraft: boolean; mode?: string;
@@ -212,53 +213,47 @@ export function ElecraftPanel({ onReportRST }: { onReportRST?: (rst: string) =>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-x-6 gap-y-2">
<label className="flex items-center gap-2 text-xs">
<span className="w-16 shrink-0 text-muted-foreground">{t('k3.power')}</span>
<input type="range" min={0} max={110} step={1} disabled={off}
<WheelRange min={0} max={110} step={5} disabled={off}
value={view.rf_power ?? 0}
onChange={(e) => put({ rf_power: Number(e.target.value) }, () => SetKenwoodPower(Number(e.target.value)))}
className="flex-1 accent-primary" />
onChange={(n) => put({ rf_power: n }, () => SetKenwoodPower(n))} />
<span className="w-12 text-right font-mono tabular-nums">{view.rf_power ?? 0} W</span>
</label>
<label className="flex items-center gap-2 text-xs">
<span className="w-16 shrink-0 text-muted-foreground flex items-center gap-1">
<AudioLines className="size-3.5" /> {t('k3.volume')}
</span>
<input type="range" min={0} max={100} step={1} disabled={off}
<WheelRange min={0} max={100} disabled={off}
value={view.af_gain ?? 0}
onChange={(e) => put({ af_gain: Number(e.target.value) }, () => SetKenwoodAFGain(Number(e.target.value)))}
className="flex-1 accent-primary" />
onChange={(n) => put({ af_gain: n }, () => SetKenwoodAFGain(n))} />
<span className="w-12 text-right font-mono tabular-nums">{view.af_gain ?? 0}</span>
</label>
<label className="flex items-center gap-2 text-xs">
<span className="w-16 shrink-0 text-muted-foreground">{t('k3.rfGain')}</span>
<input type="range" min={0} max={100} step={1} disabled={off}
<WheelRange min={0} max={100} disabled={off}
value={view.rf_gain ?? 0}
onChange={(e) => put({ rf_gain: Number(e.target.value) }, () => SetKenwoodRFGain(Number(e.target.value)))}
className="flex-1 accent-primary" />
onChange={(n) => put({ rf_gain: n }, () => SetKenwoodRFGain(n))} />
<span className="w-12 text-right font-mono tabular-nums">{view.rf_gain ?? 0}</span>
</label>
<label className="flex items-center gap-2 text-xs">
<span className="w-16 shrink-0 text-muted-foreground">{t('k3.micGain')}</span>
<input type="range" min={0} max={100} step={1} disabled={off}
<WheelRange min={0} max={100} disabled={off}
value={view.mic_gain ?? 0}
onChange={(e) => put({ mic_gain: Number(e.target.value) }, () => SetKenwoodMicGain(Number(e.target.value)))}
className="flex-1 accent-primary" />
onChange={(n) => put({ mic_gain: n }, () => SetKenwoodMicGain(n))} />
<span className="w-12 text-right font-mono tabular-nums">{view.mic_gain ?? 0}</span>
</label>
<label className="flex items-center gap-2 text-xs">
<span className="w-16 shrink-0 text-muted-foreground">{t('k3.squelch')}</span>
<input type="range" min={0} max={100} step={1} disabled={off}
<WheelRange min={0} max={100} disabled={off}
value={view.squelch ?? 0}
onChange={(e) => put({ squelch: Number(e.target.value) }, () => SetKenwoodSquelch(Number(e.target.value)))}
className="flex-1 accent-primary" />
onChange={(n) => put({ squelch: n }, () => SetKenwoodSquelch(n))} />
<span className="w-12 text-right font-mono tabular-nums">{view.squelch ?? 0}</span>
</label>
{/* CW keyer speed — the radio's own keyer, the one the K3 sends with. */}
<label className="flex items-center gap-2 text-xs">
<span className="w-16 shrink-0 text-muted-foreground">{t('k3.keySpeed')}</span>
<input type="range" min={8} max={50} step={1} disabled={off}
<WheelRange min={8} max={50} disabled={off}
value={view.key_speed || 20}
onChange={(e) => put({ key_speed: Number(e.target.value) }, () => SetKenwoodKeySpeed(Number(e.target.value)))}
className="flex-1 accent-primary" />
onChange={(n) => put({ key_speed: n }, () => SetKenwoodKeySpeed(n))} />
<span className="w-12 text-right font-mono tabular-nums">{view.key_speed || 0} wpm</span>
</label>
</div>