From ebcb898e4e70805b556a25ac0a293d67a800fc59 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Wed, 25 Jun 2025 21:02:34 +0200 Subject: feat: use mouse-based dragging for clips fixes an issue where dragging a clip would create a duplicate instead of dragging clip directly, and gives us more control moved the drag to a hook for cleaner code --- apps/web/src/components/editor/timeline.tsx | 75 ++++++++++++++--------------- 1 file changed, 36 insertions(+), 39 deletions(-) (limited to 'apps/web/src/components') diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 584a45f7..31e0dd50 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -25,6 +25,7 @@ import { import { useTimelineStore, type TimelineTrack } from "@/stores/timeline-store"; import { useMediaStore } from "@/stores/media-store"; import { usePlaybackStore } from "@/stores/playback-store"; +import { useDragClip } from "@/hooks/use-drag-clip"; import { processMediaFiles } from "@/lib/media-processing"; import { toast } from "sonner"; import { useState, useRef, useEffect, useCallback } from "react"; @@ -1163,9 +1164,19 @@ function TimelineTrackContent({ deselectClip, } = useTimelineStore(); const { currentTime } = usePlaybackStore(); + + // Mouse-based drag hook + const { + isDragging, + draggedClipId, + startDrag, + endDrag, + getDraggedClipPosition, + isValidDropTarget, + timelineRef, + } = useDragClip(zoomLevel); const [isDropping, setIsDropping] = useState(false); const [dropPosition, setDropPosition] = useState(null); - const [isDraggedOver, setIsDraggedOver] = useState(false); const [wouldOverlap, setWouldOverlap] = useState(false); const [resizing, setResizing] = useState<{ clipId: string; @@ -1240,37 +1251,14 @@ function TimelineTrackContent({ setResizing(null); }; - const handleClipDragStart = (e: React.DragEvent, clip: any) => { + const handleClipMouseDown = (e: React.MouseEvent, clip: any) => { // Calculate the offset from the left edge of the clip to where the user clicked const clipElement = e.currentTarget.parentElement as HTMLElement; const clipRect = clipElement.getBoundingClientRect(); const clickOffsetX = e.clientX - clipRect.left; const clickOffsetTime = clickOffsetX / (50 * zoomLevel); - const dragData = { - clipId: clip.id, - trackId: track.id, - name: clip.name, - clickOffsetTime: clickOffsetTime, - }; - - e.dataTransfer.setData( - "application/x-timeline-clip", - JSON.stringify(dragData) - ); - e.dataTransfer.effectAllowed = "move"; - - // Add visual feedback to the dragged element - const target = e.currentTarget.parentElement as HTMLElement; - target.style.opacity = "0.5"; - target.style.transform = "scale(0.95)"; - }; - - const handleClipDragEnd = (e: React.DragEvent) => { - // Reset visual feedback - const target = e.currentTarget.parentElement as HTMLElement; - target.style.opacity = ""; - target.style.transform = ""; + startDrag(e, clip.id, track.id, clip.startTime, clickOffsetTime); }; const handleTrackDragOver = (e: React.DragEvent) => { @@ -1388,14 +1376,12 @@ function TimelineTrackContent({ if (wouldOverlap) { e.dataTransfer.dropEffect = "none"; - setIsDraggedOver(true); setWouldOverlap(true); setDropPosition(Math.round(dropTime * 10) / 10); return; } e.dataTransfer.dropEffect = hasTimelineClip ? "move" : "copy"; - setIsDraggedOver(true); setWouldOverlap(false); setDropPosition(Math.round(dropTime * 10) / 10); }; @@ -1414,7 +1400,6 @@ function TimelineTrackContent({ dragCounterRef.current++; setIsDropping(true); - setIsDraggedOver(true); }; const handleTrackDragLeave = (e: React.DragEvent) => { @@ -1433,7 +1418,6 @@ function TimelineTrackContent({ if (dragCounterRef.current === 0) { setIsDropping(false); - setIsDraggedOver(false); setWouldOverlap(false); setDropPosition(null); } @@ -1446,7 +1430,6 @@ function TimelineTrackContent({ // Reset all drag states dragCounterRef.current = 0; setIsDropping(false); - setIsDraggedOver(false); setWouldOverlap(false); const currentDropPosition = dropPosition; setDropPosition(null); @@ -1726,10 +1709,18 @@ function TimelineTrackContent({ onDragLeave={handleTrackDragLeave} onDrop={handleTrackDrop} onMouseMove={handleResizeMove} - onMouseUp={handleResizeEnd} + onMouseUp={(e) => { + handleResizeEnd(); + if (isDragging) { + endDrag(track.id); + } + }} onMouseLeave={handleResizeEnd} > -
+
{track.clips.length === 0 ? (
c.trackId === track.id && c.clipId === clip.id ); + + const isBeingDragged = draggedClipId === clip.id; return (
{ e.stopPropagation(); @@ -1808,10 +1807,8 @@ function TimelineTrackContent({ /> {/* Clip content */}
handleClipDragStart(e, clip)} - onDragEnd={handleClipDragEnd} + className={`flex-1 relative ${isBeingDragged ? "cursor-grabbing" : "cursor-grab"}`} + onMouseDown={(e) => handleClipMouseDown(e, clip)} > {renderClipContent(clip)} {/* Clip options menu */} -- cgit v1.3.1