slightly better

This commit is contained in:
2025-10-06 02:16:23 +02:00
parent ba37b94908
commit ac772054c9
35 changed files with 1874 additions and 390 deletions

View File

@ -1,4 +1,6 @@
import { useRef, useState, useEffect } from 'react'
import { useStore } from '@nanostores/react'
import { mappingMode } from '../stores/mappingMode'
interface KnobProps {
label: string
@ -11,6 +13,9 @@ interface KnobProps {
formatValue?: (id: string, value: number) => string
valueId?: string
size?: number
paramId?: string
onMapClick?: (paramId: string, activeLFO: number) => void
mappedLFOs?: number[]
}
export function Knob({
@ -23,18 +28,30 @@ export function Knob({
onChange,
formatValue,
valueId,
size = 48
size = 48,
paramId,
onMapClick,
mappedLFOs = []
}: KnobProps) {
const [isDragging, setIsDragging] = useState(false)
const startYRef = useRef<number>(0)
const startValueRef = useRef<number>(0)
const mappingModeState = useStore(mappingMode)
const displayValue = formatValue && valueId ? formatValue(valueId, value) : `${value}${unit || ''}`
const isInMappingMode = mappingModeState.isActive && paramId
const hasMappings = mappedLFOs.length > 0
const normalizedValue = (value - min) / (max - min)
const angle = -225 + normalizedValue * 270
const handleMouseDown = (e: React.MouseEvent) => {
if (isInMappingMode && paramId && mappingModeState.activeLFO !== null && onMapClick) {
onMapClick(paramId, mappingModeState.activeLFO)
e.preventDefault()
return
}
setIsDragging(true)
startYRef.current = e.clientY
startValueRef.current = value
@ -71,7 +88,7 @@ export function Knob({
return (
<div className="relative flex flex-col items-center">
<div
className="relative cursor-ns-resize select-none"
className={`relative select-none ${isInMappingMode ? 'cursor-pointer' : 'cursor-ns-resize'}`}
onMouseDown={handleMouseDown}
style={{ width: size, height: size }}
>
@ -87,6 +104,7 @@ export function Knob({
fill="none"
stroke="white"
strokeWidth="2"
className={isInMappingMode ? 'animate-pulse' : ''}
/>
<circle
@ -105,10 +123,19 @@ export function Knob({
strokeWidth="2"
strokeLinecap="square"
/>
{hasMappings && (
<circle
cx={size / 2}
cy={8}
r={2}
fill="white"
/>
)}
</svg>
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<span className="font-mono text-[9px] tracking-[0.15em] text-white">
<span className={`font-mono text-[9px] tracking-[0.15em] text-white ${isInMappingMode ? 'animate-pulse' : ''}`}>
{isDragging ? displayValue : label.toUpperCase()}
</span>
</div>