diff --git a/components/controls/MasterFader.tsx b/components/controls/MasterFader.tsx index 67cb569..91d95b2 100644 --- a/components/controls/MasterFader.tsx +++ b/components/controls/MasterFader.tsx @@ -10,6 +10,8 @@ export interface MasterFaderProps { isClipping: boolean; onChange: (value: number) => void; onResetClip?: () => void; + onTouchStart?: () => void; + onTouchEnd?: () => void; className?: string; } @@ -20,6 +22,8 @@ export function MasterFader({ isClipping, onChange, onResetClip, + onTouchStart, + onTouchEnd, className, }: MasterFaderProps) { const [isDragging, setIsDragging] = React.useState(false); @@ -43,6 +47,7 @@ export function MasterFader({ const handleMouseDown = (e: React.MouseEvent) => { e.preventDefault(); setIsDragging(true); + onTouchStart?.(); updateValue(e.clientY); }; @@ -56,7 +61,30 @@ export function MasterFader({ const handleMouseUp = React.useCallback(() => { setIsDragging(false); - }, []); + onTouchEnd?.(); + }, [onTouchEnd]); + + const handleTouchStart = (e: React.TouchEvent) => { + e.preventDefault(); + const touch = e.touches[0]; + setIsDragging(true); + onTouchStart?.(); + updateValue(touch.clientY); + }; + + const handleTouchMove = React.useCallback( + (e: TouchEvent) => { + if (!isDragging || e.touches.length === 0) return; + const touch = e.touches[0]; + updateValue(touch.clientY); + }, + [isDragging] + ); + + const handleTouchEnd = React.useCallback(() => { + setIsDragging(false); + onTouchEnd?.(); + }, [onTouchEnd]); const updateValue = (clientY: number) => { if (!containerRef.current) return; @@ -72,12 +100,16 @@ export function MasterFader({ if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); + window.addEventListener('touchmove', handleTouchMove); + window.addEventListener('touchend', handleTouchEnd); return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); + window.removeEventListener('touchmove', handleTouchMove); + window.removeEventListener('touchend', handleTouchEnd); }; } - }, [isDragging, handleMouseMove, handleMouseUp]); + }, [isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]); return (
@@ -94,6 +126,7 @@ export function MasterFader({ ref={containerRef} className="relative w-12 h-40 bg-background/50 rounded-md border border-border/50 cursor-pointer" onMouseDown={handleMouseDown} + onTouchStart={handleTouchStart} > {/* Peak Meter (Horizontal Bar - Top) */}
diff --git a/components/tracks/TrackFader.tsx b/components/tracks/TrackFader.tsx index c1c1ff2..c2af1aa 100644 --- a/components/tracks/TrackFader.tsx +++ b/components/tracks/TrackFader.tsx @@ -60,6 +60,28 @@ export function TrackFader({ onTouchEnd?.(); }, [onTouchEnd]); + const handleTouchStart = (e: React.TouchEvent) => { + e.preventDefault(); + const touch = e.touches[0]; + setIsDragging(true); + onTouchStart?.(); + updateValue(touch.clientY); + }; + + const handleTouchMove = React.useCallback( + (e: TouchEvent) => { + if (!isDragging || e.touches.length === 0) return; + const touch = e.touches[0]; + updateValue(touch.clientY); + }, + [isDragging] + ); + + const handleTouchEnd = React.useCallback(() => { + setIsDragging(false); + onTouchEnd?.(); + }, [onTouchEnd]); + const updateValue = (clientY: number) => { if (!containerRef.current) return; @@ -74,12 +96,16 @@ export function TrackFader({ if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); + window.addEventListener('touchmove', handleTouchMove); + window.addEventListener('touchend', handleTouchEnd); return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); + window.removeEventListener('touchmove', handleTouchMove); + window.removeEventListener('touchend', handleTouchEnd); }; } - }, [isDragging, handleMouseMove, handleMouseUp]); + }, [isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]); return (
@@ -96,6 +122,7 @@ export function TrackFader({ ref={containerRef} className="relative w-10 h-32 bg-background/50 rounded-md border border-border/50 cursor-pointer" onMouseDown={handleMouseDown} + onTouchStart={handleTouchStart} > {/* Peak Meter (Horizontal Bar - Top) */}
diff --git a/components/ui/CircularKnob.tsx b/components/ui/CircularKnob.tsx index bfa4cbf..300efc5 100644 --- a/components/ui/CircularKnob.tsx +++ b/components/ui/CircularKnob.tsx @@ -91,17 +91,51 @@ export function CircularKnob({ onTouchEnd?.(); }, [onTouchEnd]); + const handleTouchStart = React.useCallback( + (e: React.TouchEvent) => { + e.preventDefault(); + const touch = e.touches[0]; + setIsDragging(true); + dragStartRef.current = { + x: touch.clientX, + y: touch.clientY, + value, + }; + onTouchStart?.(); + }, + [value, onTouchStart] + ); + + const handleTouchMove = React.useCallback( + (e: TouchEvent) => { + if (isDragging && e.touches.length > 0) { + const touch = e.touches[0]; + updateValue(touch.clientX, touch.clientY); + } + }, + [isDragging, updateValue] + ); + + const handleTouchEnd = React.useCallback(() => { + setIsDragging(false); + onTouchEnd?.(); + }, [onTouchEnd]); + React.useEffect(() => { if (isDragging) { window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); + window.addEventListener('touchmove', handleTouchMove); + window.addEventListener('touchend', handleTouchEnd); return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); + window.removeEventListener('touchmove', handleTouchMove); + window.removeEventListener('touchend', handleTouchEnd); }; } - }, [isDragging, handleMouseMove, handleMouseUp]); + }, [isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]); // Calculate rotation angle (-135deg to 135deg, 270deg range) const percentage = (value - min) / (max - min); @@ -148,6 +182,7 @@ export function CircularKnob({