From 56e6edcc5ab497388bcfad78a60681b99ca5c374 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Thu, 17 Jul 2025 17:37:18 +0200 Subject: move timeline component --- apps/web/src/components/editor/timeline.tsx | 193 ---------------------- apps/web/src/components/editor/timeline/index.tsx | 193 ++++++++++++++++++++++ 2 files changed, 193 insertions(+), 193 deletions(-) delete mode 100644 apps/web/src/components/editor/timeline.tsx create mode 100644 apps/web/src/components/editor/timeline/index.tsx diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx deleted file mode 100644 index bfc6512a..00000000 --- a/apps/web/src/components/editor/timeline.tsx +++ /dev/null @@ -1,193 +0,0 @@ -"use client"; - -import { useState, useRef, useEffect, useCallback } from "react"; -import { useTimelineStore } from "@/stores/timeline-store"; -import { usePlaybackStore } from "@/stores/playback-store"; -import { useTimelineZoom } from "@/hooks/use-timeline-zoom"; -import { useSelectionBox } from "@/hooks/use-selection-box"; -import { SnapPoint } from "@/hooks/use-timeline-snapping"; -import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; -import { TimelineToolbar } from "./timeline/timeline-toolbar"; -import { TimelineContent } from "./timeline/timeline-content"; -import { useTimelineDragHandlers } from "./timeline/timeline-drag-handlers"; -import { useTimelineActionHandlers } from "./timeline/timeline-action-handlers"; -import { useTimelineScrollSync } from "./timeline/timeline-scroll-sync"; -import { useTimelineContentClick } from "./timeline/timeline-content-click"; -import { useTimelineWheelHandler } from "./timeline/timeline-wheel-handler"; - -export function Timeline() { - // Timeline shows all tracks (video, audio, effects) and their elements. - // You can drag media here to add it to your project. - // elements can be trimmed, deleted, and moved. - - const { - tracks, - getTotalDuration, - selectedElements, - clearSelectedElements, - setSelectedElements, - dragState, - snappingEnabled, - } = useTimelineStore(); - const { currentTime, duration, seek, setDuration } = usePlaybackStore(); - const [isDragOver, setIsDragOver] = useState(false); - const [isProcessing, setIsProcessing] = useState(false); - const [progress, setProgress] = useState(0); - const timelineRef = useRef(null); - const rulerRef = useRef(null); - const [isInTimeline, setIsInTimeline] = useState(false); - - // Timeline zoom functionality - const { zoomLevel, setZoomLevel, handleWheel } = useTimelineZoom({ - containerRef: timelineRef, - isInTimeline, - }); - - // Dynamic timeline width calculation based on playhead position and duration - const dynamicTimelineWidth = Math.max( - (duration || 0) * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel, // Base width from duration - (currentTime + 30) * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel, // Width to show current time + 30 seconds buffer - timelineRef.current?.clientWidth || 1000 // Minimum width - ); - - // Scroll synchronization and auto-scroll to playhead - const rulerScrollRef = useRef(null); - const tracksScrollRef = useRef(null); - const trackLabelsRef = useRef(null); - const playheadRef = useRef(null); - const trackLabelsScrollRef = useRef(null); - - // Selection box functionality - const tracksContainerRef = useRef(null); - const { - selectionBox, - handleMouseDown: handleSelectionMouseDown, - isSelecting, - justFinishedSelecting, - } = useSelectionBox({ - containerRef: tracksContainerRef, - playheadRef, - onSelectionComplete: (elements) => { - console.log(JSON.stringify({ onSelectionComplete: elements.length })); - setSelectedElements(elements); - }, - }); - - // Calculate snap indicator state - const [currentSnapPoint, setCurrentSnapPoint] = useState( - null - ); - const showSnapIndicator = - dragState.isDragging && snappingEnabled && currentSnapPoint !== null; - - // Callback to handle snap point changes from TimelineTrackContent - const handleSnapPointChange = useCallback((snapPoint: SnapPoint | null) => { - setCurrentSnapPoint(snapPoint); - }, []); - - // Timeline content click handler - const { handleTimelineContentClick } = useTimelineContentClick({ - duration, - zoomLevel, - seek, - rulerScrollRef, - tracksScrollRef, - clearSelectedElements, - isSelecting, - justFinishedSelecting, - playheadRef, - }); - - // Update timeline duration when tracks change - useEffect(() => { - const totalDuration = getTotalDuration(); - setDuration(Math.max(totalDuration, 10)); // Minimum 10 seconds for empty timeline - }, [tracks, setDuration, getTotalDuration]); - - // Drag handlers - const { dragProps } = useTimelineDragHandlers({ - isDragOver, - setIsDragOver, - isProcessing, - setIsProcessing, - progress, - setProgress, - }); - - // Action handlers - const { - handleSplitSelected, - handleDuplicateSelected, - handleFreezeSelected, - handleSplitAndKeepLeft, - handleSplitAndKeepRight, - handleSeparateAudio, - handleDeleteSelected, - } = useTimelineActionHandlers(); - - // Scroll synchronization - useTimelineScrollSync({ - rulerScrollRef, - tracksScrollRef, - }); - - // Wheel event handling - useTimelineWheelHandler({ - timelineRef, - isInTimeline, - handleWheel, - }); - - return ( -
setIsInTimeline(true)} - onMouseLeave={() => setIsInTimeline(false)} - > - {/* Toolbar */} - - - {/* Timeline Container */} -
setIsInTimeline(true)} - onMouseLeave={() => setIsInTimeline(false)} - onMouseDown={handleSelectionMouseDown} - onClick={handleTimelineContentClick} - > - -
-
- ); -} diff --git a/apps/web/src/components/editor/timeline/index.tsx b/apps/web/src/components/editor/timeline/index.tsx new file mode 100644 index 00000000..6d90670a --- /dev/null +++ b/apps/web/src/components/editor/timeline/index.tsx @@ -0,0 +1,193 @@ +"use client"; + +import { useState, useRef, useEffect, useCallback } from "react"; +import { useTimelineStore } from "@/stores/timeline-store"; +import { usePlaybackStore } from "@/stores/playback-store"; +import { useTimelineZoom } from "@/hooks/use-timeline-zoom"; +import { useSelectionBox } from "@/hooks/use-selection-box"; +import { SnapPoint } from "@/hooks/use-timeline-snapping"; +import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants"; +import { TimelineToolbar } from "./timeline-toolbar"; +import { TimelineContent } from "./timeline-content"; +import { useTimelineDragHandlers } from "./timeline-drag-handlers"; +import { useTimelineActionHandlers } from "./timeline-action-handlers"; +import { useTimelineScrollSync } from "./timeline-scroll-sync"; +import { useTimelineContentClick } from "./timeline-content-click"; +import { useTimelineWheelHandler } from "./timeline-wheel-handler"; + +export function Timeline() { + // Timeline shows all tracks (video, audio, effects) and their elements. + // You can drag media here to add it to your project. + // elements can be trimmed, deleted, and moved. + + const { + tracks, + getTotalDuration, + selectedElements, + clearSelectedElements, + setSelectedElements, + dragState, + snappingEnabled, + } = useTimelineStore(); + const { currentTime, duration, seek, setDuration } = usePlaybackStore(); + const [isDragOver, setIsDragOver] = useState(false); + const [isProcessing, setIsProcessing] = useState(false); + const [progress, setProgress] = useState(0); + const timelineRef = useRef(null); + const rulerRef = useRef(null); + const [isInTimeline, setIsInTimeline] = useState(false); + + // Timeline zoom functionality + const { zoomLevel, setZoomLevel, handleWheel } = useTimelineZoom({ + containerRef: timelineRef, + isInTimeline, + }); + + // Dynamic timeline width calculation based on playhead position and duration + const dynamicTimelineWidth = Math.max( + (duration || 0) * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel, // Base width from duration + (currentTime + 30) * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel, // Width to show current time + 30 seconds buffer + timelineRef.current?.clientWidth || 1000 // Minimum width + ); + + // Scroll synchronization and auto-scroll to playhead + const rulerScrollRef = useRef(null); + const tracksScrollRef = useRef(null); + const trackLabelsRef = useRef(null); + const playheadRef = useRef(null); + const trackLabelsScrollRef = useRef(null); + + // Selection box functionality + const tracksContainerRef = useRef(null); + const { + selectionBox, + handleMouseDown: handleSelectionMouseDown, + isSelecting, + justFinishedSelecting, + } = useSelectionBox({ + containerRef: tracksContainerRef, + playheadRef, + onSelectionComplete: (elements) => { + console.log(JSON.stringify({ onSelectionComplete: elements.length })); + setSelectedElements(elements); + }, + }); + + // Calculate snap indicator state + const [currentSnapPoint, setCurrentSnapPoint] = useState( + null + ); + const showSnapIndicator = + dragState.isDragging && snappingEnabled && currentSnapPoint !== null; + + // Callback to handle snap point changes from TimelineTrackContent + const handleSnapPointChange = useCallback((snapPoint: SnapPoint | null) => { + setCurrentSnapPoint(snapPoint); + }, []); + + // Timeline content click handler + const { handleTimelineContentClick } = useTimelineContentClick({ + duration, + zoomLevel, + seek, + rulerScrollRef, + tracksScrollRef, + clearSelectedElements, + isSelecting, + justFinishedSelecting, + playheadRef, + }); + + // Update timeline duration when tracks change + useEffect(() => { + const totalDuration = getTotalDuration(); + setDuration(Math.max(totalDuration, 10)); // Minimum 10 seconds for empty timeline + }, [tracks, setDuration, getTotalDuration]); + + // Drag handlers + const { dragProps } = useTimelineDragHandlers({ + isDragOver, + setIsDragOver, + isProcessing, + setIsProcessing, + progress, + setProgress, + }); + + // Action handlers + const { + handleSplitSelected, + handleDuplicateSelected, + handleFreezeSelected, + handleSplitAndKeepLeft, + handleSplitAndKeepRight, + handleSeparateAudio, + handleDeleteSelected, + } = useTimelineActionHandlers(); + + // Scroll synchronization + useTimelineScrollSync({ + rulerScrollRef, + tracksScrollRef, + }); + + // Wheel event handling + useTimelineWheelHandler({ + timelineRef, + isInTimeline, + handleWheel, + }); + + return ( +
setIsInTimeline(true)} + onMouseLeave={() => setIsInTimeline(false)} + > + {/* Toolbar */} + + + {/* Timeline Container */} +
setIsInTimeline(true)} + onMouseLeave={() => setIsInTimeline(false)} + onMouseDown={handleSelectionMouseDown} + onClick={handleTimelineContentClick} + > + +
+
+ ); +} -- cgit v1.3.1