summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2025-07-17 13:38:16 +0200
committerMaze Winther <mazewinther@gmail.com>2025-07-17 13:38:16 +0200
commite577c9659f38eb9702f0c85a6bdcbc52d2720f2f (patch)
tree569f0314338f20c3bcfe11113bfcb6f274839d40 /apps/web/src/components/editor/timeline/timeline-tracks-area.tsx
parent8efc696dcddc94b1c248a099207ad97c932a545b (diff)
parentb1efc90157c87e93de422c900de74a3538040eed (diff)
downloadopencut-e577c9659f38eb9702f0c85a6bdcbc52d2720f2f.tar.gz
Merge branch 'staging' of https://github.com/mazeincoding/AppCut into staging
Diffstat (limited to 'apps/web/src/components/editor/timeline/timeline-tracks-area.tsx')
-rw-r--r--apps/web/src/components/editor/timeline/timeline-tracks-area.tsx89
1 files changed, 89 insertions, 0 deletions
diff --git a/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx b/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx
new file mode 100644
index 00000000..9242bccd
--- /dev/null
+++ b/apps/web/src/components/editor/timeline/timeline-tracks-area.tsx
@@ -0,0 +1,89 @@
+import { Fragment } from "react";
+import { Plus } from "lucide-react";
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuTrigger,
+} from "../../ui/context-menu";
+import { useTimelineStore } from "@/stores/timeline-store";
+import { TimelineTrackContent } from "../timeline-track";
+import { TrackIcon } from "./track-icon";
+import { getTrackHeight } from "@/constants/timeline-constants";
+import type { TimelineTrack } from "@/types/timeline";
+import type { SnapPoint } from "@/hooks/use-timeline-snapping";
+
+export interface TimelineTracksAreaProps {
+ tracks: TimelineTrack[];
+ zoomLevel: number;
+ handleSnapPointChange: (snapPoint: SnapPoint | null) => void;
+ clearSelectedElements: () => void;
+}
+
+export function TimelineTracksArea({
+ tracks,
+ zoomLevel,
+ handleSnapPointChange,
+ clearSelectedElements,
+}: TimelineTracksAreaProps) {
+ const { addTrack, toggleTrackMute } = useTimelineStore();
+
+ return (
+ <>
+ {/* Track Rows */}
+ {tracks.map((track, index) => (
+ <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-[98]"
+ style={{ height: `${getTrackHeight(track.type)}px` }}
+ >
+ <div className="flex items-center gap-2 px-2">
+ <TrackIcon track={track} />
+ </div>
+ {track.muted && (
+ <span className="text-xs text-red-500 font-semibold">Muted</span>
+ )}
+ </div>
+
+ {/* Scrollable Track Content */}
+ <ContextMenu>
+ <ContextMenuTrigger asChild>
+ <div
+ className=" h-full"
+ onClick={(e) => {
+ // If clicking empty area (not on a element), deselect all elements
+ if (!(e.target as HTMLElement).closest(".timeline-element")) {
+ clearSelectedElements();
+ }
+ }}
+ >
+ <TimelineTrackContent
+ track={track}
+ zoomLevel={zoomLevel}
+ onSnapPointChange={handleSnapPointChange}
+ />
+ </div>
+ </ContextMenuTrigger>
+ <ContextMenuContent>
+ <ContextMenuItem onClick={() => toggleTrackMute(track.id)}>
+ {track.muted ? "Unmute Track" : "Mute Track"}
+ </ContextMenuItem>
+ <ContextMenuItem>Track settings (soon)</ContextMenuItem>
+ </ContextMenuContent>
+ </ContextMenu>
+ </Fragment>
+ ))}
+ {/* Add Track Button - spans full width */}
+ <div
+ onClick={() => addTrack("media")}
+ className="col-span-1 sticky left-0 w-full flex items-center border-b border-muted bg-card/[0.99] hover:bg-card/50 transition-colors cursor-pointer z-[89]"
+ style={{ height: `${getTrackHeight("media")}px` }}
+ >
+ <div className="w-full flex justify-center items-center">
+ <Plus className="w-4 h-4 text-muted-foreground" />
+ </div>
+ </div>
+ </>
+ );
+}