Skip to content

Commit 7b22db9

Browse files
AmirMohammad CheraghaliAmirMohammad Cheraghali
authored andcommitted
feat(timeline): Improve Playhead usability
- Added drag-to-scrub functionality: dragging on the timeline now continuously updates playback time - Increased Playhead handle visual size and hit area - Added visual styling (border, shadow) to Playhead for better visibility
1 parent a5065fb commit 7b22db9

1 file changed

Lines changed: 42 additions & 6 deletions

File tree

src/components/VideoTimeline.tsx

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,20 @@ export const VideoTimeline = ({
7878
return Math.max(0, Math.min(duration, (relativeX / width) * duration));
7979
};
8080

81+
const [isScrubbing, setIsScrubbing] = useState(false);
82+
8183
const handleTimelineMouseDown = (e: React.MouseEvent) => {
82-
if (trimMode) return; // Don't pan in trim mode to avoid conflict with handles
83-
if (e.button === 1 || (e.button === 0 && e.altKey)) { // Middle click or Alt+Left click to pan
84+
if (trimMode) return;
85+
if (e.button === 1 || (e.button === 0 && e.altKey)) {
8486
e.preventDefault();
8587
setIsPanning(true);
8688
setPanStartX(e.clientX);
8789
if (containerRef.current) {
8890
setPanStartScroll(containerRef.current.scrollLeft);
8991
}
9092
} else if (e.button === 0) {
91-
// Normal click to seek
93+
// Scrubbing Start
94+
setIsScrubbing(true);
9295
const time = xToTime(e.clientX);
9396
onSeek(time);
9497
}
@@ -103,17 +106,48 @@ export const VideoTimeline = ({
103106
containerRef.current.scrollLeft = panStartScroll - delta;
104107
setScrollLeft(containerRef.current.scrollLeft);
105108
}
109+
110+
if (isScrubbing) {
111+
onSeek(time);
112+
}
106113
};
107114

108115
const handleMouseLeave = () => {
109116
setHoveredTime(null);
110117
setIsPanning(false);
118+
// Don't stop scrubbing on leave, let global mouse up handle it?
119+
// Or if we leave the track, maybe we should stop scrubbing if we didn't capture pointer?
120+
// Since we are using simple div events, let's keep it simple: Stop scrubbing on leave or global up.
121+
// Better UX: add global listener for scrubbing.
111122
};
112123

113124
const handleMouseUp = () => {
114125
setIsPanning(false);
126+
setIsScrubbing(false);
115127
};
116128

129+
// Global Mouse Up to catching dragging outside
130+
useEffect(() => {
131+
if (isScrubbing) {
132+
const handleGlobalUp = () => setIsScrubbing(false);
133+
const handleGlobalMove = (e: MouseEvent) => {
134+
// Calculate time relative to container
135+
if (containerRef.current) {
136+
// Logic duplicated from xToTime but needs clientX
137+
// We can reuse xToTime if we pass clientX
138+
const time = xToTime(e.clientX);
139+
onSeek(time);
140+
}
141+
};
142+
window.addEventListener('mouseup', handleGlobalUp);
143+
window.addEventListener('mousemove', handleGlobalMove);
144+
return () => {
145+
window.removeEventListener('mouseup', handleGlobalUp);
146+
window.removeEventListener('mousemove', handleGlobalMove);
147+
};
148+
}
149+
}, [isScrubbing, onSeek, duration, zoomLevel, scrollLeft]); // Re-attach if deps change
150+
117151
// Handle Zoom (Wheel)
118152
const handleWheel = (e: React.WheelEvent) => {
119153
if (e.ctrlKey || e.metaKey) {
@@ -447,12 +481,14 @@ export const VideoTimeline = ({
447481
</>
448482
)}
449483

450-
{/* Playhead */}
484+
{/* Playhead - Interactive & Larger Target */}
451485
<div
452-
className="absolute top-0 bottom-0 w-0.5 bg-red-500 z-30"
486+
className="absolute top-0 bottom-0 w-0.5 bg-red-500 z-50 pointer-events-none" // pointer-events-none lets clicks pass to track, but we handle scrubbing globally
453487
style={{ left: `${(playbackTime / duration) * 100}%` }}
454488
>
455-
<div className="absolute -top-1 -left-1.5 w-3 h-3 bg-red-500 rounded-full sticky left-0" />
489+
{/* Visual Handle */}
490+
<div className="absolute -top-1.5 -left-2 w-4 h-4 bg-red-500 rounded-full shadow-md border-2 border-white ring-2 ring-red-500/30" />
491+
{/* Hit Area for grabbing (if we wanted specific handle grab, but track grab is easier) */}
456492
</div>
457493

458494
{/* Hover Indicator */}

0 commit comments

Comments
 (0)