3118485Phase perception app: tone editor, blind same/different test, cycling tone, and site buildJeremy Magland 1type Props = {
2 values: number[]
3 min: number
4 max: number
5 step: number
6 unit?: string
7 onChange: (index: number, value: number) => void
8}
10/** One labelled slider per harmonic, with a numeric box for exact entry. */
11export function HarmonicSliders({ values, min, max, step, unit, onChange }: Props) {
12 return (
13 <div className="slider-grid">
14 {values.map((v, k) => (
15 <div className="slider-row" key={k}>
16 <span className="slider-label">{k + 1}</span>
17 <input type="range" min={min} max={max} step={step} value={v} onChange={(e) => onChange(k, Number(e.target.value))} />
18 <input
19 type="number"
20 className="slider-value"
21 min={min}
22 max={max}
23 step={step}
24 value={v}
25 onChange={(e) => {
26 const x = Number(e.target.value)
27 if (Number.isFinite(x)) onChange(k, Math.min(max, Math.max(min, x)))
28 }}
29 />
30 {unit && <span className="slider-unit">{unit}</span>}
31 </div>
32 ))}
33 </div>
34 )
35}