Skip to content

Commit a176332

Browse files
AmirMohammad CheraghaliAmirMohammad Cheraghali
authored andcommitted
feat: add 48px touch safe area to timeline playhead
1 parent 2ad8883 commit a176332

1 file changed

Lines changed: 35 additions & 5 deletions

File tree

src/components/VideoTimeline.tsx

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,31 @@ export const VideoTimeline = ({
141141
const handleGlobalMove = (e: MouseEvent) => {
142142
// Calculate time relative to container
143143
if (containerRef.current) {
144-
// Logic duplicated from xToTime but needs clientX
145-
// We can reuse xToTime if we pass clientX
146144
const time = xToTime(e.clientX);
147-
onSeek(time);
145+
if (onSeek) onSeek(time);
148146
}
149147
};
148+
const handleGlobalTouchMove = (e: TouchEvent) => {
149+
if (containerRef.current && e.touches.length > 0) {
150+
const time = xToTime(e.touches[0].clientX);
151+
if (onSeek) onSeek(time);
152+
}
153+
};
154+
150155
window.addEventListener('mouseup', handleGlobalUp);
151156
window.addEventListener('mousemove', handleGlobalMove);
157+
window.addEventListener('touchend', handleGlobalUp);
158+
window.addEventListener('touchcancel', handleGlobalUp);
159+
// using passive: false so we could potentially prevent scroll if we wanted to lock it to horizontal only,
160+
// but typical timeline behavior on mobile is okay with default touch mapping unless it scrolls
161+
window.addEventListener('touchmove', handleGlobalTouchMove, { passive: true });
162+
152163
return () => {
153164
window.removeEventListener('mouseup', handleGlobalUp);
154165
window.removeEventListener('mousemove', handleGlobalMove);
166+
window.removeEventListener('touchend', handleGlobalUp);
167+
window.removeEventListener('touchcancel', handleGlobalUp);
168+
window.removeEventListener('touchmove', handleGlobalTouchMove);
155169
};
156170
}
157171
}, [isScrubbing, onSeek, duration, zoomLevel, scrollLeft]); // Re-attach if deps change
@@ -636,8 +650,24 @@ export const VideoTimeline = ({
636650
className="absolute top-0 bottom-0 w-0.5 bg-red-500 z-50 pointer-events-none"
637651
style={{ left: `${(playbackTime / duration) * 100}%` }}
638652
>
639-
<div className="absolute top-0 -translate-y-1/2 left-1/2 -translate-x-1/2 w-5 h-5 md:w-3 md:h-3 bg-red-500 rounded-full border-2 md:border border-white shadow-md flex items-center justify-center">
640-
<div className="w-1.5 h-1.5 bg-white/90 rounded-full md:hidden" />
653+
{/* Interactive Safe Area Wrapper for Mobile Grabbing */}
654+
<div
655+
className="absolute top-0 -translate-y-1/2 left-1/2 -translate-x-1/2 w-12 h-12 md:w-6 md:h-6 flex items-center justify-center cursor-ew-resize pointer-events-auto"
656+
onMouseDown={(e) => {
657+
e.stopPropagation();
658+
e.preventDefault();
659+
setIsScrubbing(true);
660+
if (onSeek) onSeek(playbackTime);
661+
}}
662+
onTouchStart={(e) => {
663+
e.stopPropagation();
664+
setIsScrubbing(true);
665+
}}
666+
>
667+
{/* Visual Red Dot */}
668+
<div className="w-5 h-5 md:w-3 md:h-3 bg-red-500 rounded-full border-2 md:border border-white shadow-md flex items-center justify-center">
669+
<div className="w-1.5 h-1.5 bg-white/90 rounded-full md:hidden" />
670+
</div>
641671
</div>
642672
</div>
643673

0 commit comments

Comments
 (0)