import { useEffect, useRef } from 'react'; // One strip at a time — the same shape as the phone's AdjustmentPanel: a ruler // row for the open slider, an options strip for the open group, then the tab's // chip row. Chip labels are the phone's own strings (they are not translated // there either). export interface ChipDef { key: string; label: string; value?: string; // amber border while this chip's row/strip is open, amber text when the value // it carries is not its default. active?: boolean; amberValue?: boolean; pro?: boolean; disabled?: boolean; onClick: () => void; } export interface SliderDef { key: string; label: string; value: number; min: number; max: number; step: number; display: string; // amber value readout once the knob leaves its default (phone's ruler row) edited: boolean; // CSS colour the knob is currently driving (WB's temperature/tint), drawn as a // swatch under the ruler so the hue moves with the number. Absent = no swatch. swatch?: string; onChange: (value: number) => void; // "<" — closes the ruler and reopens the strip it was launched from. onBack: () => void; } export function ChipRow({ chips }: { chips: ChipDef[] }) { return (
{chips.map((chip) => ( ))}
); } export function SliderRow({ slider }: { slider: SliderDef }) { // The wheel belongs to the open ruler: one notch is one step, held inside the // parameter's own range. The listener is native because React's is passive — // the panel must not scroll away under the cursor while the value moves. const rowRef = useRef(null); const live = useRef(slider); live.current = slider; useEffect(() => { const el = rowRef.current; if (!el) return; const onWheel = (e: WheelEvent) => { e.preventDefault(); const s = live.current; const step = s.step || 1; const next = Math.min(s.max, Math.max(s.min, s.value + (e.deltaY < 0 ? step : -step))); if (next !== s.value) s.onChange(next); }; el.addEventListener('wheel', onWheel, { passive: false }); return () => el.removeEventListener('wheel', onWheel); }, []); return (
{slider.label} {slider.display}
slider.onChange(Number(e.target.value))} /> {slider.swatch ? (
{slider.swatch}
) : null}
); }