From 926aebe00423b5b0b2fd0d8cf0ee97c6e85ec1fd Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 25 Jun 2025 21:22:08 +0200 Subject: [PATCH] fix: make whole clip draggable --- apps/web/src/components/editor/timeline.tsx | 53 ++++++++++++++------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 18e1338..210a943 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -1254,8 +1254,24 @@ function TimelineTrackContent({ const [justFinishedDrag, setJustFinishedDrag] = useState(false); const handleClipMouseDown = (e: React.MouseEvent, clip: any) => { + // Handle selection first + if (!justFinishedDrag) { + const isSelected = selectedClips.some( + (c) => c.trackId === track.id && c.clipId === clip.id + ); + + if (e.metaKey || e.ctrlKey || e.shiftKey) { + // Multi-selection mode: toggle the clip + selectClip(track.id, clip.id, true); + } else if (!isSelected) { + // If clip is not selected, select it (replacing other selections) + selectClip(track.id, clip.id, false); + } + // Note: Don't deselect if already selected, as user might want to drag + } + // Calculate the offset from the left edge of the clip to where the user clicked - const clipElement = e.currentTarget.parentElement as HTMLElement; + const clipElement = e.currentTarget as HTMLElement; const clipRect = clipElement.getBoundingClientRect(); const clickOffsetX = e.clientX - clipRect.left; const clickOffsetTime = clickOffsetX / (50 * zoomLevel); @@ -1773,8 +1789,9 @@ function TimelineTrackContent({ return (
handleClipMouseDown(e, clip)} onClick={(e) => { e.stopPropagation(); @@ -1789,19 +1806,14 @@ function TimelineTrackContent({ return; // Don't handle selection when closing context menu } + // Only handle deselection here (selection is handled in mouseDown) const isSelected = selectedClips.some( (c) => c.trackId === track.id && c.clipId === clip.id ); - if (e.metaKey || e.ctrlKey || e.shiftKey) { - // Multi-selection mode: toggle the clip - selectClip(track.id, clip.id, true); - } else if (isSelected) { - // If clip is already selected, deselect it + if (isSelected && !e.metaKey && !e.ctrlKey && !e.shiftKey) { + // If clip is already selected and no modifier keys, deselect it deselectClip(track.id, clip.id); - } else { - // If clip is not selected, select it (replacing other selections) - selectClip(track.id, clip.id, false); } }} tabIndex={0} @@ -1820,13 +1832,13 @@ function TimelineTrackContent({ {/* Left trim handle */}
handleResizeStart(e, clip.id, "left")} + onMouseDown={(e) => { + e.stopPropagation(); // Prevent triggering clip drag + handleResizeStart(e, clip.id, "left"); + }} /> {/* Clip content */} -
handleClipMouseDown(e, clip)} - > +
{renderClipContent(clip)} {/* Clip options menu */}
@@ -1835,11 +1847,15 @@ function TimelineTrackContent({ size="icon" className="opacity-0 group-hover:opacity-100 transition-opacity" onClick={() => setClipMenuOpen(clip.id)} + onMouseDown={(e) => e.stopPropagation()} > {clipMenuOpen === clip.id && ( -
+
e.stopPropagation()} + >