4.0 KiB
<PluginParameterKnob>
Rotary-knob primitive for a single AudioFxPlugin parameter. The knob
implements the full keyboard contract mandated by the
<PluginSlot> accessibility requirement, exposes role="slider" with
ARIA range metadata derived from the parameter's min/max/unit
declaration, and supports an Enter-to-edit type-in affordance.
(no submodules)
Purpose
The <PluginParameterKnob> is the rotary half of the generic
parameter-control surface. Specialised plugin UIs (an EQ panel, a
compressor panel) often wrap the knob with their own visual chrome
while reusing the keyboard, ARIA, and value-management logic. The
knob is therefore deliberately presentational: the entire keyboard
keymap, value clamping, and typed-edit flow live here, while the
caller supplies the plugin and parameter declaration.
The knob subscribes to the parameter via the Phase-1
useParameter hook, so every change is reflected at the provider's
declared sampleRateHz — by default 60 Hz, matching one tick per
animation frame.
Features
role="slider",aria-valuemin,aria-valuemax,aria-valuenow,aria-valuetext(unit-aware viaformatAriaValueText).←/→fine step (1 % of range),↑/↓coarse step (10 %),PageUp/PageDown×100 page step,Home/Endjump to range endpoints.Shift×10,Alt÷10 modifier multipliers.Enteropens a numeric type-edit input;Entercommits,Escapereverts, blur commits.data-parameter-idattribute on the wrapper so plugin authors can target specific knobs from a stylesheet.- Pure CSS-class API (
rax-knob,rax-knob__dial,rax-knob__readout,rax-knob__input) — no styling assumptions baked in.
Usage: Basic
import { PluginParameterKnob } from "@suaveplan/react-audio/components/plugin-parameter-knob";
function GainKnob({ plugin }) {
const param = plugin.parameterIndex.get("gain")!;
return <PluginParameterKnob plugin={plugin} parameter={param} />;
}
Usage: Advanced
// Iterate every parameter on a plugin, rendering each with a custom label
// override and theming via the className API.
function PluginPanel({ plugin }) {
return (
<div className="grid grid-cols-4 gap-4">
{plugin.parameters.map((p) => (
<PluginParameterKnob
key={p.id}
plugin={plugin}
parameter={p}
label={p.name.toUpperCase()}
className="ring-1 ring-zinc-700 rounded-md p-2"
/>
))}
</div>
);
}
API Reference
Props
plugin: AudioFxPlugin— the plugin instance whose parameter is bound.parameter: PluginParameter— the declaration to render. The parameter MUST be present inplugin.parameterIndex; otherwise the underlyinguseParameterhook throwsREACT_AUDIO.UNKNOWN_PARAMETER.label?: string— optional visible label override; defaults toparameter.name.className?: string— optional CSS class appended to the wrapper.
Behaviour
- Each parameter update flows through
param.set(...), which delegates toplugin.setParameter. Value subscriptions go through the Phase-1useParameterhook. - The typed-edit input is rendered only when the user presses
Enteron the knob; it auto-focuses, supportsEnter/Escape/blur, and clamps the parsed value to the declared range.
Implementation Notes
- The keyboard handler delegates to the pure
resolveParameterKeyboardChangehelper, which is shared with<PluginParameterSlider>to guarantee identical step semantics between the two primitives. formatAriaValueTextis called withsignedDb: trueso dB readouts carry the+sign — matching the convention used by every other audio tool that ships unit-aware screen-reader text.- No animation libraries are imported; visual rotation should be
composed by the consumer via CSS. The component respects
prefers-reduced-motionby virtue of being CSS-only.