Files
bruitiste/src/components/ui/Slider.tsx
2025-10-06 14:31:05 +02:00

82 lines
2.1 KiB
TypeScript

import { useStore } from '@nanostores/react'
import { mappingMode } from '../../stores/mappingMode'
interface SliderProps {
label: string
value: number
min: number
max: number
step: number
unit?: string
onChange: (value: number) => void
formatValue?: (id: string, value: number) => string
valueId?: string
paramId?: string
onMapClick?: (paramId: string, activeLFO: number) => void
mappedLFOs?: number[]
}
export function Slider({
label,
value,
min,
max,
step,
unit,
onChange,
formatValue,
valueId,
paramId,
onMapClick,
mappedLFOs = []
}: SliderProps) {
const mappingModeState = useStore(mappingMode)
const formatNumber = (num: number) => {
if (Number.isInteger(num)) return num.toString()
return num.toFixed(1)
}
const displayValue = formatValue && valueId
? formatValue(valueId, value)
: `${formatNumber(value)}${unit || ''}`
const isInMappingMode = !!(mappingModeState.isActive && paramId)
const hasMappings = mappedLFOs.length > 0
const handleClick = () => {
if (isInMappingMode && paramId && mappingModeState.activeLFO !== null && onMapClick) {
onMapClick(paramId, mappingModeState.activeLFO)
}
}
return (
<div
className={`flex flex-col gap-2 ${isInMappingMode ? 'cursor-pointer' : ''}`}
onClick={handleClick}
>
<div className="flex justify-between items-baseline">
<label className={`font-mono text-[10px] tracking-[0.2em] ${
isInMappingMode ? 'text-white animate-pulse' : hasMappings ? 'text-white' : 'text-white'
}`}>
{label.toUpperCase()}
{hasMappings && <span className="ml-1 text-[8px]"></span>}
</label>
<span className="font-mono text-[10px] text-white">
{displayValue}
</span>
</div>
<input
type="range"
min={min}
max={max}
step={step}
value={value}
onChange={(e) => onChange(Number(e.target.value))}
className={`w-full h-[2px] appearance-none cursor-pointer slider ${
hasMappings ? 'bg-white opacity-80' : 'bg-white'
}`}
disabled={isInMappingMode}
/>
</div>
)
}