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:
@@ -0,0 +1,62 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// A range slider the mouse wheel can move.
|
||||
//
|
||||
// Dragging a 4-pixel-tall slider to change the power by five watts is a fussy
|
||||
// gesture; rolling the wheel over it is not, and it is what an operator reaches
|
||||
// for after using the Flex and Yaesu consoles, which have had it for a while.
|
||||
//
|
||||
// React's own onWheel is registered PASSIVE, so preventDefault() inside it is
|
||||
// ignored and the panel scrolls under the pointer while the value changes. The
|
||||
// listener therefore has to be attached natively with { passive: false }, and
|
||||
// the live values read through refs — the listener is installed once and would
|
||||
// otherwise capture the first render's value for ever.
|
||||
//
|
||||
// The Flex and Yaesu panels each grew their own copy of this before it was worth
|
||||
// sharing. They are left alone deliberately: they work, they are in daily use,
|
||||
// and their styling differs in small ways that a merge would have to guess at.
|
||||
export function WheelRange({ value, onChange, min = 0, max = 100, step = 1, disabled, accent = 'var(--primary)', className }: {
|
||||
value: number; onChange: (v: number) => void;
|
||||
min?: number; max?: number; step?: number; disabled?: boolean; accent?: string; className?: string;
|
||||
}) {
|
||||
const v = Math.max(min, Math.min(max, value));
|
||||
const pct = max > min ? ((v - min) / (max - min)) * 100 : 0;
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const valRef = useRef(v); valRef.current = v;
|
||||
const cbRef = useRef(onChange); cbRef.current = onChange;
|
||||
const disRef = useRef(disabled); disRef.current = disabled;
|
||||
const stepRef = useRef(step); stepRef.current = step;
|
||||
const minRef = useRef(min); minRef.current = min;
|
||||
const maxRef = useRef(max); maxRef.current = max;
|
||||
|
||||
useEffect(() => {
|
||||
const el = ref.current;
|
||||
if (!el) return;
|
||||
const onWheel = (e: WheelEvent) => {
|
||||
if (disRef.current) return;
|
||||
e.preventDefault();
|
||||
const d = e.deltaY < 0 ? stepRef.current : -stepRef.current;
|
||||
const nv = Math.max(minRef.current, Math.min(maxRef.current, valRef.current + d));
|
||||
if (nv !== valRef.current) cbRef.current(nv);
|
||||
};
|
||||
el.addEventListener('wheel', onWheel, { passive: false });
|
||||
return () => el.removeEventListener('wheel', onWheel);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={ref}
|
||||
type="range" min={min} max={max} step={step} value={v} disabled={disabled}
|
||||
onChange={(e) => onChange(parseInt(e.target.value, 10))}
|
||||
className={cn('flex-1 h-1.5 rounded-full appearance-none cursor-pointer disabled:opacity-30 disabled:cursor-default',
|
||||
'[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:size-3.5 [&::-webkit-slider-thumb]:rounded-full',
|
||||
'[&::-webkit-slider-thumb]:bg-card [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:cursor-pointer',
|
||||
className)}
|
||||
style={{
|
||||
background: `linear-gradient(to right, ${accent} ${pct}%, color-mix(in srgb, var(--foreground) 18%, transparent) ${pct}%)`,
|
||||
borderColor: accent,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user