diff options
Diffstat (limited to 'apps/web/src/hooks/timeline/use-bookmark-drag.ts')
| -rw-r--r-- | apps/web/src/hooks/timeline/use-bookmark-drag.ts | 278 |
1 files changed, 0 insertions, 278 deletions
diff --git a/apps/web/src/hooks/timeline/use-bookmark-drag.ts b/apps/web/src/hooks/timeline/use-bookmark-drag.ts deleted file mode 100644 index 3ff1cb21..00000000 --- a/apps/web/src/hooks/timeline/use-bookmark-drag.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { - useState, - useCallback, - useEffect, - useRef, - type RefObject, -} from "react"; -import { useEditor } from "@/hooks/use-editor"; -import { useShiftKey } from "@/hooks/use-shift-key"; -import { TIMELINE_DRAG_THRESHOLD_PX } from "@/components/editor/panels/timeline/interaction"; -import { roundToFrame } from "opencut-wasm"; -import { getMouseTimeFromClientX } from "@/lib/timeline/drag-utils"; -import { - findSnapPoints, - snapToNearestPoint, - type SnapPoint, -} from "@/lib/timeline/snap-utils"; -import type { Bookmark } from "@/lib/timeline"; - -export interface BookmarkDragState { - isDragging: boolean; - bookmarkTime: number | null; - currentTime: number; -} - -interface PendingBookmarkDrag { - bookmarkTime: number; - startMouseX: number; - startMouseY: number; -} - -interface UseBookmarkDragProps { - zoomLevel: number; - scrollRef: RefObject<HTMLElement | null>; - snappingEnabled: boolean; - onSnapPointChange?: (snapPoint: SnapPoint | null) => void; -} - -export function useBookmarkDrag({ - zoomLevel, - scrollRef, - snappingEnabled, - onSnapPointChange, -}: UseBookmarkDragProps) { - const editor = useEditor(); - const isShiftHeldRef = useShiftKey(); - const tracks = editor.scenes.getActiveScene().tracks; - const activeScene = editor.scenes.getActiveScene(); - const bookmarks = activeScene?.bookmarks ?? []; - const playheadTime = editor.playback.getCurrentTime(); - const duration = editor.timeline.getTotalDuration(); - - const [dragState, setDragState] = useState<BookmarkDragState>({ - isDragging: false, - bookmarkTime: null, - currentTime: 0, - }); - const [isPendingDrag, setIsPendingDrag] = useState(false); - const pendingDragRef = useRef<PendingBookmarkDrag | null>(null); - const lastMouseXRef = useRef(0); - - const startDrag = useCallback( - ({ - bookmarkTime, - initialCurrentTime, - }: { - bookmarkTime: number; - initialCurrentTime: number; - }) => { - setDragState({ - isDragging: true, - bookmarkTime, - currentTime: initialCurrentTime, - }); - }, - [], - ); - - const endDrag = useCallback(() => { - setDragState({ - isDragging: false, - bookmarkTime: null, - currentTime: 0, - }); - }, []); - - const getSnapResult = useCallback( - ({ - rawTime, - excludeBookmarkTime, - }: { - rawTime: number; - excludeBookmarkTime: number; - }): { snappedTime: number; snapPoint: SnapPoint | null } => { - const shouldSnap = snappingEnabled && !isShiftHeldRef.current; - if (!shouldSnap) { - return { snappedTime: rawTime, snapPoint: null }; - } - - const snapPoints = findSnapPoints({ - tracks, - playheadTime, - bookmarks, - excludeBookmarkTime, - }); - const result = snapToNearestPoint({ - targetTime: rawTime, - snapPoints, - zoomLevel, - }); - return { - snappedTime: result.snappedTime, - snapPoint: result.snapPoint, - }; - }, - [snappingEnabled, tracks, playheadTime, bookmarks, zoomLevel, isShiftHeldRef], - ); - - useEffect(() => { - if (!dragState.isDragging && !isPendingDrag) return; - - const handleMouseMove = (event: MouseEvent) => { - lastMouseXRef.current = event.clientX; - - const scrollContainer = scrollRef.current; - if (!scrollContainer) return; - - if (isPendingDrag && pendingDragRef.current) { - const { startMouseX, startMouseY, bookmarkTime } = - pendingDragRef.current; - const deltaX = Math.abs(event.clientX - startMouseX); - const deltaY = Math.abs(event.clientY - startMouseY); - - if ( - deltaX <= TIMELINE_DRAG_THRESHOLD_PX && - deltaY <= TIMELINE_DRAG_THRESHOLD_PX - ) { - return; - } - - const activeProject = editor.project.getActive(); - if (!activeProject) return; - - const scrollLeft = scrollContainer.scrollLeft; - const mouseTime = getMouseTimeFromClientX({ - clientX: event.clientX, - containerRect: scrollContainer.getBoundingClientRect(), - zoomLevel, - scrollLeft, - }); - const frameSnappedTime = roundToFrame({ time: Math.max(0, Math.min(mouseTime, duration)), rate: activeProject.settings.fps }) ?? Math.max(0, Math.min(mouseTime, duration)); - const { snappedTime: initialTime } = getSnapResult({ - rawTime: frameSnappedTime, - excludeBookmarkTime: bookmarkTime, - }); - - startDrag({ - bookmarkTime, - initialCurrentTime: initialTime, - }); - pendingDragRef.current = null; - setIsPendingDrag(false); - return; - } - - if (!dragState.isDragging || dragState.bookmarkTime === null) return; - - const activeProject = editor.project.getActive(); - if (!activeProject) return; - - const scrollLeft = scrollContainer.scrollLeft; - const mouseTime = getMouseTimeFromClientX({ - clientX: event.clientX, - containerRect: scrollContainer.getBoundingClientRect(), - zoomLevel, - scrollLeft, - }); - const clampedTime = Math.max(0, Math.min(mouseTime, duration)); - const frameSnappedTime = roundToFrame({ time: clampedTime, rate: activeProject.settings.fps }) ?? clampedTime; - const snapResult = getSnapResult({ - rawTime: frameSnappedTime, - excludeBookmarkTime: dragState.bookmarkTime, - }); - - setDragState((previousDragState) => ({ - ...previousDragState, - currentTime: snapResult.snappedTime, - })); - onSnapPointChange?.(snapResult.snapPoint); - }; - - document.addEventListener("mousemove", handleMouseMove); - return () => document.removeEventListener("mousemove", handleMouseMove); - }, [ - dragState.isDragging, - dragState.bookmarkTime, - zoomLevel, - duration, - editor.project, - scrollRef, - isPendingDrag, - startDrag, - getSnapResult, - onSnapPointChange, - ]); - - useEffect(() => { - if (!dragState.isDragging) return; - - const handleMouseUp = () => { - if (dragState.bookmarkTime === null) { - endDrag(); - onSnapPointChange?.(null); - return; - } - - const clampedTime = Math.max( - 0, - Math.min(dragState.currentTime, duration), - ); - - editor.scenes.moveBookmark({ - fromTime: dragState.bookmarkTime, - toTime: clampedTime, - }); - - endDrag(); - onSnapPointChange?.(null); - }; - - document.addEventListener("mouseup", handleMouseUp); - return () => document.removeEventListener("mouseup", handleMouseUp); - }, [ - dragState.isDragging, - dragState.bookmarkTime, - dragState.currentTime, - duration, - endDrag, - onSnapPointChange, - editor.scenes, - ]); - - useEffect(() => { - if (!isPendingDrag) return; - - const handleMouseUp = () => { - pendingDragRef.current = null; - setIsPendingDrag(false); - onSnapPointChange?.(null); - }; - - document.addEventListener("mouseup", handleMouseUp); - return () => document.removeEventListener("mouseup", handleMouseUp); - }, [isPendingDrag, onSnapPointChange]); - - const handleBookmarkMouseDown = useCallback( - ({ event, bookmark }: { event: React.MouseEvent; bookmark: Bookmark }) => { - if (event.button !== 0) return; - - event.preventDefault(); - event.stopPropagation(); - - pendingDragRef.current = { - bookmarkTime: bookmark.time, - startMouseX: event.clientX, - startMouseY: event.clientY, - }; - setIsPendingDrag(true); - }, - [], - ); - - return { - dragState, - handleBookmarkMouseDown, - lastMouseXRef, - }; -} |
