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({