summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/snap-indicator.tsx
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2025-07-26 22:25:57 +0200
committerMaze Winther <mazewinther@gmail.com>2025-07-26 22:25:57 +0200
commitd154937ac2fdf5877095c4a0f1076cba04d7c3d1 (patch)
treef8485b9682e0e9108313c7d9b370b176e4ae55d5 /apps/web/src/components/editor/snap-indicator.tsx
parent906f95399d3a0119af0e57c2cb31fafbcdc31391 (diff)
downloadopencut-d154937ac2fdf5877095c4a0f1076cba04d7c3d1.tar.gz
feat: lock playhead to frame (instead of floating) and add autoscroll to timeline
Diffstat (limited to 'apps/web/src/components/editor/snap-indicator.tsx')
-rw-r--r--apps/web/src/components/editor/snap-indicator.tsx29
1 files changed, 27 insertions, 2 deletions
diff --git a/apps/web/src/components/editor/snap-indicator.tsx b/apps/web/src/components/editor/snap-indicator.tsx
index 302a52bb..053d119e 100644
--- a/apps/web/src/components/editor/snap-indicator.tsx
+++ b/apps/web/src/components/editor/snap-indicator.tsx
@@ -3,6 +3,7 @@
import { SnapPoint } from "@/hooks/use-timeline-snapping";
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
import type { TimelineTrack } from "@/types/timeline";
+import { useState, useEffect } from "react";
interface SnapIndicatorProps {
snapPoint: SnapPoint | null;
@@ -11,6 +12,7 @@ interface SnapIndicatorProps {
tracks: TimelineTrack[];
timelineRef: React.RefObject<HTMLDivElement>;
trackLabelsRef?: React.RefObject<HTMLDivElement>;
+ tracksScrollRef: React.RefObject<HTMLDivElement>;
}
export function SnapIndicator({
@@ -20,7 +22,29 @@ export function SnapIndicator({
tracks,
timelineRef,
trackLabelsRef,
+ tracksScrollRef,
}: SnapIndicatorProps) {
+ const [scrollLeft, setScrollLeft] = useState(0);
+
+ // Track scroll position to lock snap indicator to frame
+ useEffect(() => {
+ const tracksViewport = tracksScrollRef.current?.querySelector(
+ "[data-radix-scroll-area-viewport]"
+ ) as HTMLElement;
+
+ if (!tracksViewport) return;
+
+ const handleScroll = () => {
+ setScrollLeft(tracksViewport.scrollLeft);
+ };
+
+ // Set initial scroll position
+ setScrollLeft(tracksViewport.scrollLeft);
+
+ tracksViewport.addEventListener("scroll", handleScroll);
+ return () => tracksViewport.removeEventListener("scroll", handleScroll);
+ }, [tracksScrollRef]);
+
if (!isVisible || !snapPoint) {
return null;
}
@@ -34,9 +58,10 @@ export function SnapIndicator({
? trackLabelsRef.current.offsetWidth
: 0;
- const leftPosition =
- trackLabelsWidth +
+ // Calculate position locked to timeline content (accounting for scroll)
+ const timelinePosition =
snapPoint.time * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
+ const leftPosition = trackLabelsWidth + timelinePosition - scrollLeft;
return (
<div