82 lines
2.1 KiB
TypeScript
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>
|
|
)
|
|
} |