summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline/timeline-ruler.tsx
diff options
context:
space:
mode:
authorMaze <167211895+mazeincoding@users.noreply.github.com>2026-01-31 00:20:04 +0100
committerGitHub <noreply@github.com>2026-01-31 00:20:04 +0100
commit7bf0984698e43b9aa2dd879f65951318ed410390 (patch)
treeccf2cd590a47d550152afc07129cb55c71a9ec35 /apps/web/src/components/editor/timeline/timeline-ruler.tsx
parent0173db99448d1c4c6415dcf15272e17551b48b04 (diff)
downloadopencut-7bf0984698e43b9aa2dd879f65951318ed410390.tar.gz
codebase overhaul (#697)
Diffstat (limited to 'apps/web/src/components/editor/timeline/timeline-ruler.tsx')
-rw-r--r--apps/web/src/components/editor/timeline/timeline-ruler.tsx83
1 files changed, 83 insertions, 0 deletions
diff --git a/apps/web/src/components/editor/timeline/timeline-ruler.tsx b/apps/web/src/components/editor/timeline/timeline-ruler.tsx
new file mode 100644
index 00000000..87702a4f
--- /dev/null
+++ b/apps/web/src/components/editor/timeline/timeline-ruler.tsx
@@ -0,0 +1,83 @@
+import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
+import { DEFAULT_FPS } from "@/constants/project-constants";
+import { useEditor } from "@/hooks/use-editor";
+import { getRulerConfig, shouldShowLabel } from "@/lib/timeline/ruler-utils";
+import { TimelineTick } from "./timeline-tick";
+
+interface TimelineRulerProps {
+ zoomLevel: number;
+ dynamicTimelineWidth: number;
+ rulerRef: React.RefObject<HTMLDivElement>;
+ 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,
+ handleWheel,
+ handleTimelineContentClick,
+ handleRulerTrackingMouseDown,
+ handleRulerMouseDown,
+}: TimelineRulerProps) {
+ const editor = useEditor();
+ const duration = editor.timeline.getTotalDuration();
+ const pixelsPerSecond = TIMELINE_CONSTANTS.PIXELS_PER_SECOND * zoomLevel;
+ const visibleDuration = dynamicTimelineWidth / pixelsPerSecond;
+ const effectiveDuration = Math.max(duration, visibleDuration);
+ const project = editor.project.getActive();
+ const fps = project?.settings.fps ?? DEFAULT_FPS;
+ const { labelIntervalSeconds, tickIntervalSeconds } = getRulerConfig({
+ zoomLevel,
+ fps,
+ });
+ const tickCount = Math.ceil(effectiveDuration / tickIntervalSeconds) + 1;
+
+ const timelineTicks: Array<JSX.Element> = [];
+ for (let tickIndex = 0; tickIndex < tickCount; tickIndex += 1) {
+ const time = tickIndex * tickIntervalSeconds;
+ if (time > effectiveDuration) break;
+
+ const showLabel = shouldShowLabel({ time, labelIntervalSeconds });
+ timelineTicks.push(
+ <TimelineTick
+ key={tickIndex}
+ time={time}
+ zoomLevel={zoomLevel}
+ fps={fps}
+ showLabel={showLabel}
+ />,
+ );
+ }
+
+ return (
+ <div
+ role="slider"
+ tabIndex={0}
+ aria-label="Timeline ruler"
+ aria-valuemin={0}
+ aria-valuemax={effectiveDuration}
+ aria-valuenow={0}
+ className="relative h-4 flex-1 overflow-x-visible"
+ onWheel={handleWheel}
+ onClick={handleTimelineContentClick}
+ onMouseDown={handleRulerTrackingMouseDown}
+ onKeyDown={() => {}}
+ >
+ <div
+ role="none"
+ ref={rulerRef}
+ className="relative h-4 cursor-default select-none"
+ style={{
+ width: `${dynamicTimelineWidth}px`,
+ }}
+ onMouseDown={handleRulerMouseDown}
+ >
+ {timelineTicks}
+ </div>
+ </div>
+ );
+}