fix: click empty area on timeline to move playhead

This commit is contained in:
Maze Winther
2025-08-04 12:35:14 +02:00
parent efa5be2ab8
commit 0386624ff5
+102 -15
View File
@@ -167,11 +167,32 @@ export function Timeline() {
const handleTimelineMouseDown = useCallback((e: React.MouseEvent) => { const handleTimelineMouseDown = useCallback((e: React.MouseEvent) => {
// Only track mouse down on timeline background areas (not elements) // Only track mouse down on timeline background areas (not elements)
const target = e.target as HTMLElement; const target = e.target as HTMLElement;
console.log(
JSON.stringify({
debug_mousedown: "START",
target_class: target.className,
target_parent_class: target.parentElement?.className,
clientX: e.clientX,
clientY: e.clientY,
timeStamp: e.timeStamp,
})
);
const isTimelineBackground = const isTimelineBackground =
!target.closest(".timeline-element") && !target.closest(".timeline-element") &&
!playheadRef.current?.contains(target) && !playheadRef.current?.contains(target) &&
!target.closest("[data-track-labels]"); !target.closest("[data-track-labels]");
console.log(
JSON.stringify({
debug_mousedown: "CHECK",
isTimelineBackground,
hasTimelineElement: !!target.closest(".timeline-element"),
hasPlayhead: !!playheadRef.current?.contains(target),
hasTrackLabels: !!target.closest("[data-track-labels]"),
})
);
if (isTimelineBackground) { if (isTimelineBackground) {
mouseTrackingRef.current = { mouseTrackingRef.current = {
isMouseDown: true, isMouseDown: true,
@@ -179,12 +200,38 @@ export function Timeline() {
downY: e.clientY, downY: e.clientY,
downTime: e.timeStamp, downTime: e.timeStamp,
}; };
console.log(
JSON.stringify({
debug_mousedown: "TRACKED",
mouseTracking: mouseTrackingRef.current,
})
);
} else {
console.log(
JSON.stringify({
debug_mousedown: "IGNORED - not timeline background",
})
);
} }
}, []); }, []);
// Timeline content click to seek handler // Timeline content click to seek handler
const handleTimelineContentClick = useCallback( const handleTimelineContentClick = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
console.log(
JSON.stringify({
debug_click: "START",
target: (e.target as HTMLElement).className,
target_parent: (e.target as HTMLElement).parentElement?.className,
mouseTracking: mouseTrackingRef.current,
isSelecting,
justFinishedSelecting,
clickX: e.clientX,
clickY: e.clientY,
timeStamp: e.timeStamp,
})
);
const { isMouseDown, downX, downY, downTime } = mouseTrackingRef.current; const { isMouseDown, downX, downY, downTime } = mouseTrackingRef.current;
// Reset mouse tracking // Reset mouse tracking
@@ -199,8 +246,8 @@ export function Timeline() {
if (!isMouseDown) { if (!isMouseDown) {
console.log( console.log(
JSON.stringify({ JSON.stringify({
ignoredClickWithoutMouseDown: true, debug_click: "REJECTED - no mousedown",
timeStamp: e.timeStamp, mouseTracking: mouseTrackingRef.current,
}) })
); );
return; return;
@@ -214,11 +261,10 @@ export function Timeline() {
if (deltaX > 5 || deltaY > 5 || deltaTime > 500) { if (deltaX > 5 || deltaY > 5 || deltaTime > 500) {
console.log( console.log(
JSON.stringify({ JSON.stringify({
ignoredDragNotClick: true, debug_click: "REJECTED - movement too large",
deltaX, deltaX,
deltaY, deltaY,
deltaTime, deltaTime,
timeStamp: e.timeStamp,
}) })
); );
return; return;
@@ -226,27 +272,54 @@ export function Timeline() {
// Don't seek if this was a selection box operation // Don't seek if this was a selection box operation
if (isSelecting || justFinishedSelecting) { if (isSelecting || justFinishedSelecting) {
console.log(
JSON.stringify({
debug_click: "REJECTED - selection operation",
isSelecting,
justFinishedSelecting,
})
);
return; return;
} }
// Don't seek if clicking on timeline elements, but still deselect // Don't seek if clicking on timeline elements, but still deselect
if ((e.target as HTMLElement).closest(".timeline-element")) { if ((e.target as HTMLElement).closest(".timeline-element")) {
console.log(
JSON.stringify({
debug_click: "REJECTED - clicked timeline element",
})
);
return; return;
} }
// Don't seek if clicking on playhead // Don't seek if clicking on playhead
if (playheadRef.current?.contains(e.target as Node)) { if (playheadRef.current?.contains(e.target as Node)) {
console.log(
JSON.stringify({
debug_click: "REJECTED - clicked playhead",
})
);
return; return;
} }
// Don't seek if clicking on track labels // Don't seek if clicking on track labels
if ((e.target as HTMLElement).closest("[data-track-labels]")) { if ((e.target as HTMLElement).closest("[data-track-labels]")) {
console.log(
JSON.stringify({
debug_click: "REJECTED - clicked track labels",
})
);
clearSelectedElements(); clearSelectedElements();
return; return;
} }
// Clear selected elements when clicking empty timeline area // Clear selected elements when clicking empty timeline area
console.log(JSON.stringify({ clearingSelectedElements: true })); console.log(
JSON.stringify({
debug_click: "PROCEEDING - clearing elements",
clearingSelectedElements: true,
})
);
clearSelectedElements(); clearSelectedElements();
// Determine if we're clicking in ruler or tracks area // Determine if we're clicking in ruler or tracks area
@@ -254,24 +327,39 @@ export function Timeline() {
"[data-ruler-area]" "[data-ruler-area]"
); );
console.log(
JSON.stringify({
debug_click: "CALCULATING POSITION",
isRulerClick,
clientX: e.clientX,
clientY: e.clientY,
target_element: (e.target as HTMLElement).tagName,
target_class: (e.target as HTMLElement).className,
})
);
let mouseX: number; let mouseX: number;
let scrollLeft = 0; let scrollLeft = 0;
if (isRulerClick) { if (isRulerClick) {
// Calculate based on ruler position // Calculate based on ruler position
const rulerContent = rulerScrollRef.current?.querySelector( const rulerContent = rulerScrollRef.current;
"[data-radix-scroll-area-viewport]" if (!rulerContent) {
) as HTMLElement; console.log(
if (!rulerContent) return; JSON.stringify({
debug_click: "ERROR - no ruler container found",
})
);
return;
}
const rect = rulerContent.getBoundingClientRect(); const rect = rulerContent.getBoundingClientRect();
mouseX = e.clientX - rect.left; mouseX = e.clientX - rect.left;
scrollLeft = rulerContent.scrollLeft; scrollLeft = rulerContent.scrollLeft;
} else { } else {
// Calculate based on tracks content position const tracksContent = tracksScrollRef.current;
const tracksContent = tracksScrollRef.current?.querySelector( if (!tracksContent) {
"[data-radix-scroll-area-viewport]" return;
) as HTMLElement; }
if (!tracksContent) return;
const rect = tracksContent.getBoundingClientRect(); const rect = tracksContent.getBoundingClientRect();
mouseX = e.clientX - rect.left; mouseX = e.clientX - rect.left;
scrollLeft = tracksContent.scrollLeft; scrollLeft = tracksContent.scrollLeft;
@@ -289,7 +377,6 @@ export function Timeline() {
// Use frame snapping for timeline clicking // Use frame snapping for timeline clicking
const projectFps = activeProject?.fps || 30; const projectFps = activeProject?.fps || 30;
const time = snapTimeToFrame(rawTime, projectFps); const time = snapTimeToFrame(rawTime, projectFps);
seek(time); seek(time);
}, },
[ [