diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-07-17 13:38:16 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-07-17 13:38:16 +0200 |
| commit | e577c9659f38eb9702f0c85a6bdcbc52d2720f2f (patch) | |
| tree | 569f0314338f20c3bcfe11113bfcb6f274839d40 /apps/web/src/components/editor/timeline/timeline-content-click.tsx | |
| parent | 8efc696dcddc94b1c248a099207ad97c932a545b (diff) | |
| parent | b1efc90157c87e93de422c900de74a3538040eed (diff) | |
| download | opencut-e577c9659f38eb9702f0c85a6bdcbc52d2720f2f.tar.gz | |
Merge branch 'staging' of https://github.com/mazeincoding/AppCut into staging
Diffstat (limited to 'apps/web/src/components/editor/timeline/timeline-content-click.tsx')
| -rw-r--r-- | apps/web/src/components/editor/timeline/timeline-content-click.tsx | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/apps/web/src/components/editor/timeline/timeline-content-click.tsx b/apps/web/src/components/editor/timeline/timeline-content-click.tsx new file mode 100644 index 00000000..7f4676c7 --- /dev/null +++ b/apps/web/src/components/editor/timeline/timeline-content-click.tsx @@ -0,0 +1,118 @@ +import { useCallback } from "react"; +import { useProjectStore } from "@/stores/project-store"; +import { + TIMELINE_CONSTANTS, + snapTimeToFrame, +} from "@/constants/timeline-constants"; + +export interface TimelineContentClickProps { + duration: number; + zoomLevel: number; + seek: (time: number) => void; + rulerScrollRef: React.RefObject<HTMLDivElement>; + tracksScrollRef: React.RefObject<HTMLDivElement>; + clearSelectedElements: () => void; + isSelecting: boolean; + justFinishedSelecting: boolean; + playheadRef: React.RefObject<HTMLDivElement>; +} + +export function useTimelineContentClick({ + duration, + zoomLevel, + seek, + rulerScrollRef, + tracksScrollRef, + clearSelectedElements, + isSelecting, + justFinishedSelecting, + playheadRef, +}: TimelineContentClickProps) { + const { activeProject } = useProjectStore(); + + // Timeline content click to seek handler + const handleTimelineContentClick = useCallback( + (e: React.MouseEvent) => { + // Don't seek if this was a selection box operation + if (isSelecting || justFinishedSelecting) { + return; + } + + // Don't seek if clicking on timeline elements, but still deselect + if ((e.target as HTMLElement).closest(".timeline-element")) { + return; + } + + // Don't seek if clicking on playhead + if (playheadRef.current?.contains(e.target as Node)) { + return; + } + + // Don't seek if clicking on track labels + if ((e.target as HTMLElement).closest("[data-track-labels]")) { + clearSelectedElements(); + return; + } + + // Clear selected elements when clicking empty timeline area + clearSelectedElements(); + + // Determine if we're clicking in ruler or tracks area + const isRulerClick = (e.target as HTMLElement).closest( + "[data-ruler-area]", + ); + + let mouseX: number; + let scrollLeft = 0; + + if (isRulerClick) { + // Calculate based on ruler position + const rulerContent = rulerScrollRef.current?.querySelector( + "[data-radix-scroll-area-viewport]", + ) as HTMLElement; + if (!rulerContent) return; + const rect = rulerContent.getBoundingClientRect(); + mouseX = e.clientX - rect.left; + scrollLeft = rulerContent.scrollLeft; + } else { + // Calculate based on tracks content position + const tracksContent = tracksScrollRef.current?.querySelector( + "[data-radix-scroll-area-viewport]", + ) as HTMLElement; + if (!tracksContent) return; + const rect = tracksContent.getBoundingClientRect(); + mouseX = e.clientX - rect.left; + scrollLeft = tracksContent.scrollLeft; + } + + const rawTime = Math.max( + 0, + Math.min( + duration, + (mouseX + scrollLeft) / + (TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel), + ), + ); + + // Use frame snapping for timeline clicking + const projectFps = activeProject?.fps || 30; + const time = snapTimeToFrame(rawTime, projectFps); + + seek(time); + }, + [ + duration, + zoomLevel, + seek, + rulerScrollRef, + tracksScrollRef, + clearSelectedElements, + isSelecting, + justFinishedSelecting, + playheadRef, + activeProject?.fps, + ], + ); + + return { handleTimelineContentClick }; +} |
