diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-20 11:31:17 +0200 |
| commit | 3e89d2998588f8e35c524e523ba2ee853f0f6475 (patch) | |
| tree | 57221f96c1259b3b07e3ddf8c1adefc502b9d8eb /apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx | |
| parent | 729d10592f4e802f91c555c309ff3ba4b119f843 (diff) | |
| download | opencut-3e89d2998588f8e35c524e523ba2ee853f0f6475.tar.gz | |
refactor: restructure files to their domains, new preview overlay system, and dep graph
Diffstat (limited to 'apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx')
| -rw-r--r-- | apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx | 141 |
1 files changed, 0 insertions, 141 deletions
diff --git a/apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx b/apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx deleted file mode 100644 index 7b69e820..00000000 --- a/apps/web/src/components/editor/panels/timeline/timeline-ruler.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { type JSX, useLayoutEffect, useRef } from "react"; -import { - BASE_TIMELINE_PIXELS_PER_SECOND, -} from "@/lib/timeline/scale"; -import { mediaTimeToSeconds } from "opencut-wasm"; -import { TICKS_PER_SECOND } from "@/lib/wasm"; -import { TIMELINE_RULER_HEIGHT_PX } from "./layout"; -import { DEFAULT_FPS } from "@/lib/fps/defaults"; -import { useEditor } from "@/hooks/use-editor"; -import { getRulerConfig, shouldShowLabel } from "@/lib/timeline/ruler-utils"; -import { useScrollPosition } from "@/hooks/timeline/use-scroll-position"; -import { TimelineTick } from "./timeline-tick"; - -interface TimelineRulerProps { - zoomLevel: number; - dynamicTimelineWidth: number; - rulerRef: React.Ref<HTMLDivElement>; - tracksScrollRef: React.RefObject<HTMLElement | null>; - handleWheel: (e: React.WheelEvent) => void; - handleTimelineContentClick: (e: React.MouseEvent) => void; - handleRulerTrackingMouseDown: (e: React.MouseEvent) => void; - handleRulerMouseDown: (e: React.MouseEvent) => void; -} - -export function TimelineRuler({ - zoomLevel, - dynamicTimelineWidth, - rulerRef, - tracksScrollRef, - handleWheel, - handleTimelineContentClick, - handleRulerTrackingMouseDown, - handleRulerMouseDown, -}: TimelineRulerProps) { - const durationTicks = useEditor((e) => e.timeline.getTotalDuration()); - const durationSeconds = mediaTimeToSeconds({ time: durationTicks }); - const pixelsPerSecond = BASE_TIMELINE_PIXELS_PER_SECOND * zoomLevel; - const visibleDurationSeconds = dynamicTimelineWidth / pixelsPerSecond; - const effectiveDurationSeconds = Math.max(durationSeconds, visibleDurationSeconds); - const fps = - useEditor((e) => e.project.getActiveOrNull()?.settings.fps) ?? DEFAULT_FPS; - const { labelIntervalSeconds, tickIntervalSeconds } = getRulerConfig({ - zoomLevel, - fps, - }); - const tickCount = Math.ceil(effectiveDurationSeconds / tickIntervalSeconds) + 1; - - const { scrollLeft, viewportWidth } = useScrollPosition({ - scrollRef: tracksScrollRef, - }); - - /** - * widens the virtualization buffer during zoom transitions. - * useScrollPosition lags one frame behind the scroll adjustment - * that useLayoutEffect applies after a zoom change. - */ - const prevZoomRef = useRef(zoomLevel); - const isZoomTransition = zoomLevel !== prevZoomRef.current; - const bufferPx = isZoomTransition - ? Math.max(200, (scrollLeft + viewportWidth) * 0.15) - : 200; - - useLayoutEffect(() => { - prevZoomRef.current = zoomLevel; - }, [zoomLevel]); - - const visibleStartTimeSeconds = Math.max( - 0, - (scrollLeft - bufferPx) / pixelsPerSecond, - ); - const visibleEndTimeSeconds = - (scrollLeft + viewportWidth + bufferPx) / pixelsPerSecond; - - const startTickIndex = Math.max( - 0, - Math.floor(visibleStartTimeSeconds / tickIntervalSeconds), - ); - const endTickIndex = Math.min( - tickCount - 1, - Math.ceil(visibleEndTimeSeconds / tickIntervalSeconds), - ); - - const timelineTicks: Array<JSX.Element> = []; - for ( - let tickIndex = startTickIndex; - tickIndex <= endTickIndex; - tickIndex += 1 - ) { - const timeSeconds = tickIndex * tickIntervalSeconds; - if (timeSeconds > effectiveDurationSeconds) break; - - const timeTicks = Math.round(timeSeconds * TICKS_PER_SECOND); - const showLabel = shouldShowLabel({ time: timeSeconds, labelIntervalSeconds }); - timelineTicks.push( - <TimelineTick - key={tickIndex} - time={timeTicks} - timeInSeconds={timeSeconds} - zoomLevel={zoomLevel} - fps={fps} - showLabel={showLabel} - />, - ); - } - - return ( - <div - role="slider" - tabIndex={0} - aria-label="Timeline ruler" - aria-valuemin={0} - aria-valuemax={effectiveDurationSeconds} - aria-valuenow={0} - className="relative flex-1 overflow-x-visible" - style={{ height: TIMELINE_RULER_HEIGHT_PX }} - onWheel={handleWheel} - onClick={(event) => { - // Ruler seek already happens on mousedown via playhead scrubbing. - // Forwarding the follow-up click re-enters the selection-clearing path. - if (event.target === event.currentTarget) { - handleTimelineContentClick(event); - } - }} - onMouseDown={handleRulerTrackingMouseDown} - onKeyDown={() => {}} - > - <div - role="none" - ref={rulerRef} - className="relative cursor-default select-none" - style={{ - height: TIMELINE_RULER_HEIGHT_PX, - width: `${dynamicTimelineWidth}px`, - }} - onMouseDown={handleRulerMouseDown} - > - {timelineTicks} - </div> - </div> - ); -} |
