diff options
| author | Simon Orzel <simonorzel@gmail.com> | 2025-07-17 02:53:25 +0200 |
|---|---|---|
| committer | Simon Orzel <simonorzel@gmail.com> | 2025-07-17 02:53:25 +0200 |
| commit | 1505c1b12f516c65b6a3d076f0e9623a294031d6 (patch) | |
| tree | 3bf87f9358e24fcec719dd07c8975fb41f33aac4 /apps/web | |
| parent | 98b2103286f04d67db1d57f9e3330efa5d41fdb7 (diff) | |
| download | opencut-1505c1b12f516c65b6a3d076f0e9623a294031d6.tar.gz | |
feat: timeline changes
Diffstat (limited to 'apps/web')
7 files changed, 48 insertions, 93 deletions
diff --git a/apps/web/src/components/editor/timeline.tsx b/apps/web/src/components/editor/timeline.tsx index 932896f4..bfc6512a 100644 --- a/apps/web/src/components/editor/timeline.tsx +++ b/apps/web/src/components/editor/timeline.tsx @@ -129,7 +129,6 @@ export function Timeline() { useTimelineScrollSync({ rulerScrollRef, tracksScrollRef, - trackLabelsScrollRef, }); // Wheel event handling diff --git a/apps/web/src/components/editor/timeline/index.ts b/apps/web/src/components/editor/timeline/index.ts deleted file mode 100644 index 984293ce..00000000 --- a/apps/web/src/components/editor/timeline/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export { Timeline } from "../timeline"; -export { TimelineToolbar } from "./timeline-toolbar"; -export { TimelineContent } from "./timeline-content"; -export { TimelineRuler } from "./timeline-ruler"; -export { TimelineTracksArea } from "./timeline-tracks-area"; -export { TrackIcon } from "./track-icon"; -export { useTimelineDragHandlers } from "./timeline-drag-handlers"; -export { useTimelineActionHandlers } from "./timeline-action-handlers"; -export { useTimelineScrollSync } from "./timeline-scroll-sync"; -export { useTimelineContentClick } from "./timeline-content-click"; -export { useTimelineWheelHandler } from "./timeline-wheel-handler"; diff --git a/apps/web/src/components/editor/timeline/timeline-playhead.tsx b/apps/web/src/components/editor/timeline/timeline-playhead.tsx index a4d035f6..9cd1429c 100644 --- a/apps/web/src/components/editor/timeline/timeline-playhead.tsx +++ b/apps/web/src/components/editor/timeline/timeline-playhead.tsx @@ -59,27 +59,39 @@ export function TimelinePlayhead({ const leftPosition = playheadPosition * TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
return (
- <div
- ref={playheadRef}
- className="absolute pointer-events-auto z-[95]"
- style={{
- left: `${trackLabelsWidth + leftPosition}px`,
- top: 0,
- bottom: 0,
- width: "2px", // Slightly wider for better click target
- }}
- onMouseDown={handlePlayheadMouseDown}
- >
- {/* The playhead line spanning full height */}
+ <>
+ {/* Playhead line container */}
<div
- className={`absolute left-0 w-0.5 cursor-col-resize h-full ${isSnappingToPlayhead ? "bg-primary" : "bg-foreground"}`}
- />
+ ref={playheadRef}
+ className="absolute pointer-events-auto z-[95]"
+ style={{
+ left: `${trackLabelsWidth + leftPosition}px`,
+ top: 0,
+ bottom: 0,
+ width: "2px", // Slightly wider for better click target
+ }}
+ onMouseDown={handlePlayheadMouseDown}
+ >
+ {/* The playhead line spanning full height */}
+ <div
+ className={`absolute left-0 w-0.5 cursor-col-resize h-full ${isSnappingToPlayhead ? "bg-primary" : "bg-foreground"}`}
+ />
+ </div>
- {/* Playhead dot indicator at the top (in ruler area) */}
+ {/* Playhead dot indicator - separate container with highest z-index */}
<div
- className={`sticky top-1 left-1 -translate-x-[40%] transform w-3 h-3 rounded-full shadow-sm ${isSnappingToPlayhead ? "bg-primary border-primary" : "bg-foreground border-foreground"}`}
- />
- </div>
+ className="absolute pointer-events-none z-[100]"
+ style={{
+ left: `${trackLabelsWidth + leftPosition}px`,
+ top: 0,
+ bottom: 0,
+ }}
+ >
+ <div
+ className={`sticky top-1 left-1 -translate-x-[40%] transform w-3 h-3 rounded-full shadow-sm ${isSnappingToPlayhead ? "bg-primary border-primary" : "bg-foreground border-foreground"}`}
+ />
+ </div>
+ </>
);
}
diff --git a/apps/web/src/components/editor/timeline/timeline-ruler.tsx b/apps/web/src/components/editor/timeline/timeline-ruler.tsx index 07a4ad5d..a875fe0f 100644 --- a/apps/web/src/components/editor/timeline/timeline-ruler.tsx +++ b/apps/web/src/components/editor/timeline/timeline-ruler.tsx @@ -83,7 +83,7 @@ export function TimelineRuler({ markers.push( <div key={i} - className={`absolute top-0 bottom-0 z-[97] ${ + className={`absolute top-0 bottom-0 z-[99] ${ isMainMarker ? "border-l border-muted-foreground/40" : "border-l border-muted-foreground/20" @@ -91,7 +91,7 @@ export function TimelineRuler({ style={{ left: `${leftPosition}px` }} > <span - className={`absolute top-1 left-1 text-[0.6rem] z-[97] ${ + className={`absolute top-1 left-1 text-[0.6rem] z-[99] ${ isMainMarker ? "text-muted-foreground font-medium" : "text-muted-foreground/70" @@ -108,14 +108,14 @@ export function TimelineRuler({ return ( <div - className="sticky top-0 bg-card/[0.99] border-b border-muted/30 z-[97]" + className="sticky top-0 bg-card/[0.99] border-b border-muted/30 z-[99]" onMouseDown={handleSelectionMouseDown} onClick={handleTimelineContentClick} data-ruler-area > <div ref={rulerRef} - className="relative h-5 select-none cursor-default pb-1 z-[97]" + className="relative h-5 select-none cursor-default pb-1 z-[99]" onMouseDown={handleRulerMouseDown} > {rulerMarkers} diff --git a/apps/web/src/components/editor/timeline/timeline-scroll-sync.tsx b/apps/web/src/components/editor/timeline/timeline-scroll-sync.tsx index 2318f977..473c6f3e 100644 --- a/apps/web/src/components/editor/timeline/timeline-scroll-sync.tsx +++ b/apps/web/src/components/editor/timeline/timeline-scroll-sync.tsx @@ -3,20 +3,16 @@ import { useEffect, useRef } from "react"; export interface TimelineScrollSyncProps { rulerScrollRef: React.RefObject<HTMLDivElement>; tracksScrollRef: React.RefObject<HTMLDivElement>; - trackLabelsScrollRef: React.RefObject<HTMLDivElement>; } export function useTimelineScrollSync({ rulerScrollRef, tracksScrollRef, - trackLabelsScrollRef, }: TimelineScrollSyncProps) { const isUpdatingRef = useRef(false); - const lastRulerSync = useRef(0); - const lastTracksSync = useRef(0); - const lastVerticalSync = useRef(0); + const lastSyncRef = useRef(0); - // --- Scroll synchronization effect --- + // --- Horizontal scroll synchronization between ruler and tracks --- useEffect(() => { const rulerViewport = rulerScrollRef.current?.querySelector( "[data-radix-scroll-area-viewport]" @@ -24,76 +20,38 @@ export function useTimelineScrollSync({ const tracksViewport = tracksScrollRef.current?.querySelector( "[data-radix-scroll-area-viewport]" ) as HTMLElement; - const trackLabelsViewport = trackLabelsScrollRef.current?.querySelector( - "[data-radix-scroll-area-viewport]" - ) as HTMLElement; if (!rulerViewport || !tracksViewport) return; - // Horizontal scroll synchronization between ruler and tracks + // Throttled scroll handlers for better performance const handleRulerScroll = () => { const now = Date.now(); - if (isUpdatingRef.current || now - lastRulerSync.current < 16) return; - lastRulerSync.current = now; + if (isUpdatingRef.current || now - lastSyncRef.current < 16) return; // 60fps throttling + lastSyncRef.current = now; + isUpdatingRef.current = true; tracksViewport.scrollLeft = rulerViewport.scrollLeft; isUpdatingRef.current = false; }; + const handleTracksScroll = () => { const now = Date.now(); - if (isUpdatingRef.current || now - lastTracksSync.current < 16) return; - lastTracksSync.current = now; + if (isUpdatingRef.current || now - lastSyncRef.current < 16) return; // 60fps throttling + lastSyncRef.current = now; + isUpdatingRef.current = true; rulerViewport.scrollLeft = tracksViewport.scrollLeft; isUpdatingRef.current = false; }; - rulerViewport.addEventListener("scroll", handleRulerScroll); - tracksViewport.addEventListener("scroll", handleTracksScroll); - - // Vertical scroll synchronization between track labels and tracks content - if (trackLabelsViewport) { - const handleTrackLabelsScroll = () => { - const now = Date.now(); - if (isUpdatingRef.current || now - lastVerticalSync.current < 16) - return; - lastVerticalSync.current = now; - isUpdatingRef.current = true; - tracksViewport.scrollTop = trackLabelsViewport.scrollTop; - isUpdatingRef.current = false; - }; - const handleTracksVerticalScroll = () => { - const now = Date.now(); - if (isUpdatingRef.current || now - lastVerticalSync.current < 16) - return; - lastVerticalSync.current = now; - isUpdatingRef.current = true; - trackLabelsViewport.scrollTop = tracksViewport.scrollTop; - isUpdatingRef.current = false; - }; - - trackLabelsViewport.addEventListener("scroll", handleTrackLabelsScroll); - tracksViewport.addEventListener("scroll", handleTracksVerticalScroll); - - return () => { - rulerViewport.removeEventListener("scroll", handleRulerScroll); - tracksViewport.removeEventListener("scroll", handleTracksScroll); - trackLabelsViewport.removeEventListener( - "scroll", - handleTrackLabelsScroll - ); - tracksViewport.removeEventListener( - "scroll", - handleTracksVerticalScroll - ); - }; - } + rulerViewport.addEventListener("scroll", handleRulerScroll, { passive: true }); + tracksViewport.addEventListener("scroll", handleTracksScroll, { passive: true }); return () => { rulerViewport.removeEventListener("scroll", handleRulerScroll); tracksViewport.removeEventListener("scroll", handleTracksScroll); }; - }, []); + }, [rulerScrollRef, tracksScrollRef]); return null; } diff --git a/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx b/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx index 0e11f366..144eb0f3 100644 --- a/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx +++ b/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx @@ -35,7 +35,7 @@ export function TimelineTracksArea({ <Fragment key={track.id}> {/* Left Column (Sticky Track Labels) */} <div - className="sticky left-0 flex items-center border-b border-panel border-inset group bg-card/[0.99] z-[96]" + className="sticky left-0 flex items-center border-b border-panel border-inset group bg-card/[0.99] z-[98]" style={{ height: `${getTrackHeight(track.type)}px` }} > <div className="flex items-center gap-2 px-2"> diff --git a/apps/web/src/components/editor/timeline/timeline-wheel-handler.tsx b/apps/web/src/components/editor/timeline/timeline-wheel-handler.tsx index 430d65ae..417cbbe8 100644 --- a/apps/web/src/components/editor/timeline/timeline-wheel-handler.tsx +++ b/apps/web/src/components/editor/timeline/timeline-wheel-handler.tsx @@ -11,17 +11,14 @@ export function useTimelineWheelHandler({ isInTimeline, handleWheel, }: TimelineWheelHandlerProps) { - // Add wheel event listeners with passive: false to allow preventDefault useEffect(() => { const timelineContainer = timelineRef.current; if (!timelineContainer || !isInTimeline) return; const handleWheelCapture = (e: WheelEvent) => { - // Call the existing handleWheel function handleWheel(e as any); }; - // Add wheel event listener with passive: false to allow preventDefault timelineContainer.addEventListener("wheel", handleWheelCapture, { passive: false }); return () => { |
