first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
# `a11y/keyboard-bindings`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Pure-value table mapping keyboard keys to parameter-control actions
(`fine-decrement`, `coarse-increment`, `page-decrement`, `min`,
`max`, `enter-value`). Phase-2 components — knobs, sliders, the
parameter inspector — consume this table directly so the keymap is
declared in exactly one place and easy to audit for accessibility
compliance.
The module is React-free; it exports a frozen array, two factor
constants (`KEY_MODIFIER_FACTOR_SHIFT` = 10, `KEY_MODIFIER_FACTOR_ALT`
= 0.1), and a helper `matchKeyBinding(event)` that resolves a
`KeyboardEvent`-shaped value into `{ binding, compositeFactor }` or
`undefined` for a no-match.
## Features
- `PARAMETER_KEY_BINDINGS` — frozen `ReadonlyArray<KeyBinding>`.
- `KEY_MODIFIER_FACTOR_SHIFT`, `KEY_MODIFIER_FACTOR_ALT` — composable
multipliers.
- `matchKeyBinding(event)` — pure function, no DOM imports.
- Standard a11y keymap (arrows, page-up/down, home, end, enter).
## Usage: Basic
```ts
import { matchKeyBinding } from "@suaveplan/react-audio/a11y";
function onKey(event: KeyboardEvent) {
const match = matchKeyBinding(event);
if (match === undefined) return;
console.log(match.binding.action, match.compositeFactor);
}
```
## Usage: Advanced
```tsx
import {
matchKeyBinding,
PARAMETER_KEY_BINDINGS,
} from "@suaveplan/react-audio/a11y";
function Knob({
value,
min,
max,
step,
onChange,
}: {
value: number;
min: number;
max: number;
step: number;
onChange: (next: number) => void;
}) {
return (
<div
role="slider"
aria-valuemin={min}
aria-valuemax={max}
aria-valuenow={value}
tabIndex={0}
onKeyDown={(event) => {
const match = matchKeyBinding(event);
if (match === undefined) return;
event.preventDefault();
const delta = step * match.compositeFactor;
switch (match.binding.action) {
case "fine-decrement":
case "coarse-decrement":
case "page-decrement":
onChange(Math.max(min, value - delta));
return;
case "fine-increment":
case "coarse-increment":
case "page-increment":
onChange(Math.min(max, value + delta));
return;
case "min":
onChange(min);
return;
case "max":
onChange(max);
return;
default:
return;
}
}}
>
{value}
</div>
);
}
```
## API Reference
- `KeyBinding` — `{ key, action, factor, modifiers? }`.
- `PARAMETER_KEY_BINDINGS: ReadonlyArray<KeyBinding>`.
- `KEY_MODIFIER_FACTOR_SHIFT: 10`.
- `KEY_MODIFIER_FACTOR_ALT: 0.1`.
- `matchKeyBinding(event): { binding, compositeFactor } | undefined`.
## Implementation Notes
The keymap conventions (`←` fine, `↓` coarse, `Shift` ×10, `Alt`
×0.1) match the design.md accessibility section so the upstream
behaviour carries forward. The `matchKeyBinding` helper takes an
event-shaped object rather than a `KeyboardEvent` so it is callable
from test harnesses with plain object literals — and zero DOM types
are imported into this file. The factor multiplication
order is `binding.factor * shift * alt`; with both modifiers pressed
the net effect is `factor × 10 × 0.1 = factor`, which is the
intended "shift cancels alt" semantics.