Files
skills/docs/ui/react/react-audio/src/components/plugin-parameter-knob/plugin-parameter-knob.md
2026-05-29 08:20:56 +01:00

4.0 KiB
Raw Blame History

<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 via formatAriaValueText).
  • ←/→ fine step (1 % of range), ↑/↓ coarse step (10 %), PageUp/PageDown ×100 page step, Home/End jump to range endpoints.
  • Shift ×10, Alt ÷10 modifier multipliers.
  • Enter opens a numeric type-edit input; Enter commits, Escape reverts, blur commits.
  • data-parameter-id attribute 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 in plugin.parameterIndex; otherwise the underlying useParameter hook throws REACT_AUDIO.UNKNOWN_PARAMETER.
  • label?: string — optional visible label override; defaults to parameter.name.
  • className?: string — optional CSS class appended to the wrapper.

Behaviour

  • Each parameter update flows through param.set(...), which delegates to plugin.setParameter. Value subscriptions go through the Phase-1 useParameter hook.
  • The typed-edit input is rendered only when the user presses Enter on the knob; it auto-focuses, supports Enter/Escape/blur, and clamps the parsed value to the declared range.

Implementation Notes

  • The keyboard handler delegates to the pure resolveParameterKeyboardChange helper, which is shared with <PluginParameterSlider> to guarantee identical step semantics between the two primitives.
  • formatAriaValueText is called with signedDb: true so 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-motion by virtue of being CSS-only.