diff options
| author | Maze Winther <mazewinther@gmail.com> | 2025-07-10 23:55:08 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2025-07-10 23:55:08 +0200 |
| commit | bb65d4fb96d3726448100ea8d95e5eab92e6ddba (patch) | |
| tree | bc5a0c9378b25a8d758ed8b3bacc4957b97bddc6 /apps/web/src/components/editor/timeline-playhead.tsx | |
| parent | eabcdb0988c3fdf5f940cb24a3e817bb19d45512 (diff) | |
| download | opencut-bb65d4fb96d3726448100ea8d95e5eab92e6ddba.tar.gz | |
refactor: timeline playhead component and hook
Diffstat (limited to 'apps/web/src/components/editor/timeline-playhead.tsx')
| -rw-r--r-- | apps/web/src/components/editor/timeline-playhead.tsx | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/apps/web/src/components/editor/timeline-playhead.tsx b/apps/web/src/components/editor/timeline-playhead.tsx new file mode 100644 index 00000000..51dab0f3 --- /dev/null +++ b/apps/web/src/components/editor/timeline-playhead.tsx @@ -0,0 +1,125 @@ +"use client";
+
+import { TimelineTrack } from "@/types/timeline";
+import {
+ TIMELINE_CONSTANTS,
+ getTotalTracksHeight,
+} from "@/constants/timeline-constants";
+import { useTimelinePlayhead } from "@/hooks/use-timeline-playhead";
+
+interface TimelinePlayheadProps {
+ currentTime: number;
+ duration: number;
+ zoomLevel: number;
+ tracks: TimelineTrack[];
+ seek: (time: number) => void;
+ rulerRef: React.RefObject<HTMLDivElement>;
+ rulerScrollRef: React.RefObject<HTMLDivElement>;
+ tracksScrollRef: React.RefObject<HTMLDivElement>;
+}
+
+export function TimelinePlayhead({
+ currentTime,
+ duration,
+ zoomLevel,
+ tracks,
+ seek,
+ rulerRef,
+ rulerScrollRef,
+ tracksScrollRef,
+}: TimelinePlayheadProps) {
+ const { playheadPosition, handlePlayheadMouseDown } = useTimelinePlayhead({
+ currentTime,
+ duration,
+ zoomLevel,
+ seek,
+ rulerRef,
+ rulerScrollRef,
+ tracksScrollRef,
+ });
+
+ return (
+ <>
+ {/* Playhead in ruler (scrubbable) */}
+ <div
+ className="playhead absolute top-0 bottom-0 w-0.5 bg-red-500 pointer-events-auto z-50 cursor-col-resize"
+ style={{
+ left: `${playheadPosition * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel}px`,
+ }}
+ onMouseDown={handlePlayheadMouseDown}
+ >
+ <div className="absolute top-1 left-1/2 transform -translate-x-1/2 w-3 h-3 bg-red-500 rounded-full border-2 border-white shadow-sm" />
+ </div>
+ </>
+ );
+}
+
+interface TimelinePlayheadTracksProps {
+ currentTime: number;
+ duration: number;
+ zoomLevel: number;
+ tracks: TimelineTrack[];
+ seek: (time: number) => void;
+ rulerRef: React.RefObject<HTMLDivElement>;
+ rulerScrollRef: React.RefObject<HTMLDivElement>;
+ tracksScrollRef: React.RefObject<HTMLDivElement>;
+}
+
+export function TimelinePlayheadTracks({
+ currentTime,
+ duration,
+ zoomLevel,
+ tracks,
+ seek,
+ rulerRef,
+ rulerScrollRef,
+ tracksScrollRef,
+}: TimelinePlayheadTracksProps) {
+ const { playheadPosition, handlePlayheadMouseDown } = useTimelinePlayhead({
+ currentTime,
+ duration,
+ zoomLevel,
+ seek,
+ rulerRef,
+ rulerScrollRef,
+ tracksScrollRef,
+ });
+
+ if (tracks.length === 0) return null;
+
+ return (
+ <div
+ className="absolute top-0 w-0.5 bg-red-500 pointer-events-auto z-50 cursor-col-resize"
+ style={{
+ left: `${playheadPosition * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel}px`,
+ height: `${getTotalTracksHeight(tracks)}px`,
+ }}
+ onMouseDown={handlePlayheadMouseDown}
+ />
+ );
+}
+
+// Also export a hook for getting ruler handlers
+export function useTimelinePlayheadRuler({
+ currentTime,
+ duration,
+ zoomLevel,
+ seek,
+ rulerRef,
+ rulerScrollRef,
+ tracksScrollRef,
+}: Omit<TimelinePlayheadProps, "tracks" | "dynamicTimelineWidth">) {
+ const { handleRulerMouseDown, isDraggingRuler } = useTimelinePlayhead({
+ currentTime,
+ duration,
+ zoomLevel,
+ seek,
+ rulerRef,
+ rulerScrollRef,
+ tracksScrollRef,
+ });
+
+ return { handleRulerMouseDown, isDraggingRuler };
+}
+
+export { TimelinePlayhead as default };
|
