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,109 @@
# `<SliderV>`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Vertical slider control. Pointer interaction sets the value directly
from `clientY` against the rendered track rect — drag is a single
continuous gesture from `pointerdown` through `pointermove` until
`pointerup` / `pointercancel`. Keyboard handling delegates to the
shared `keyboardStep` helper so the up/down behaviour stays in lockstep
with `<SliderH>` (which uses left/right). Detents snap nearby values
to caller-provided breakpoints, useful for mix-engineering faders
that want a hard 0 dB stop.
## Features
- Pointer capture for jitter-free drag past the SVG bounds.
- Optional detent snapping with a per-control or default threshold.
- Keyboard accessibility: arrow keys, Home, End, PageUp, PageDown,
Shift fine-mode.
- `aria-slider` with `aria-orientation="vertical"`.
- CSS-variable theming (`--cs-slider-track-color`,
`--cs-slider-thumb-color`, `--cs-slider-detent-color`).
- Auto-rendered detent ticks on the track.
- Step-quantized output via `step` (default `0.01`).
## Usage: Basic
```tsx
import { useState } from "react";
import { SliderV } from "@suaveplan/react-control-surface";
export function VolumeSlider() {
const [v, setV] = useState(0.5);
return (
<SliderV
value={v}
onChange={setV}
min={0}
max={1}
step={0.01}
label="Volume"
/>
);
}
```
## Usage: Advanced
Mixer-style fader in dB with detents at -∞ / -20 / 0:
```tsx
import { useState } from "react";
import { SliderV } from "@suaveplan/react-control-surface";
export function ChannelFader() {
const [db, setDb] = useState(0);
return (
<SliderV
value={db}
onChange={setDb}
min={-60}
max={12}
step={0.5}
label="Channel 1"
detents={[
{ at: -60, label: "-∞" },
{ at: -20, label: "-20" },
{ at: 0, label: "0 dB" },
]}
snapThreshold={0.5}
trackPx={240}
thicknessPx={32}
/>
);
}
```
## API Reference
| Prop | Type | Default |
| ---------------- | --------------------- | -------- |
| `value` | `number` | required |
| `onChange` | `(v: number) => void` | required |
| `min` | `number` | required |
| `max` | `number` | required |
| `step` | `number` | `0.01` |
| `disabled` | `boolean` | `false` |
| `label` | `string` | — |
| `ariaLabel` | `string` | — |
| `detents` | `SliderDetent[]` | — |
| `snapThreshold` | `number` | 2% span |
| `trackPx` | `number` | `160` |
| `thicknessPx` | `number` | `24` |
| `fineModeFactor` | `number` | `0.1` |
| `className` | `string` | — |
## Implementation Notes
The slider derives value from the rendered SVG rect (not the bare
`trackPx`) so it remains accurate when the host container resizes it
via CSS. The internal helper module `slider-helpers.ts` is shared
verbatim with `<SliderH>`; the orientation flip lives entirely inside
`keyboardStep` and the visual rendering. When the rect height
collapses to 0 (offscreen / display:none), the math falls back to
`trackPx` so drag interactions continue to produce a sensible value.