summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline/timeline-content-click.tsx
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2025-07-17 13:38:16 +0200
committerMaze Winther <mazewinther@gmail.com>2025-07-17 13:38:16 +0200
commite577c9659f38eb9702f0c85a6bdcbc52d2720f2f (patch)
tree569f0314338f20c3bcfe11113bfcb6f274839d40 /apps/web/src/components/editor/timeline/timeline-content-click.tsx
parent8efc696dcddc94b1c248a099207ad97c932a545b (diff)
parentb1efc90157c87e93de422c900de74a3538040eed (diff)
downloadopencut-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.tsx118
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 };
+}