summaryrefslogtreecommitdiff
path: root/apps/web/src/components/editor/timeline/timeline-content.tsx
blob: ed0375da204236dbd297045d5fb0ab9f48e113d6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { SelectionBox } from "../selection-box";
import { TimelinePlayhead } from "./timeline-playhead";
import { SnapIndicator } from "../snap-indicator";
import { TimelineRuler } from "./timeline-ruler";
import { TimelineTracksArea } from "./timeline-tracks-area";
import type { TimelineTrack } from "@/types/timeline";
import type { SnapPoint } from "@/hooks/use-timeline-snapping";

export interface TimelineContentProps {
  dynamicTimelineWidth: number;
  tracks: TimelineTrack[];
  duration: number;
  zoomLevel: number;
  currentTime: number;
  seek: (time: number) => void;
  rulerRef: React.RefObject<HTMLDivElement>;
  rulerScrollRef: React.RefObject<HTMLDivElement>;
  tracksScrollRef: React.RefObject<HTMLDivElement>;
  playheadRef: React.RefObject<HTMLDivElement>;
  trackLabelsRef: React.RefObject<HTMLDivElement>;
  timelineRef: React.RefObject<HTMLDivElement>;
  handleSelectionMouseDown: (e: React.MouseEvent) => void;
  handleTimelineContentClick: (e: React.MouseEvent) => void;
  handleSnapPointChange: (snapPoint: SnapPoint | null) => void;
  clearSelectedElements: () => void;
  selectionBox: {
    startPos: { x: number; y: number } | null;
    currentPos: { x: number; y: number } | null;
    isActive: boolean;
  } | null;
  tracksContainerRef: React.RefObject<HTMLDivElement>;
  currentSnapPoint: SnapPoint | null;
  showSnapIndicator: boolean;
}

export function TimelineContent({
  dynamicTimelineWidth,
  tracks,
  duration,
  zoomLevel,
  currentTime,
  seek,
  rulerRef,
  rulerScrollRef,
  tracksScrollRef,
  playheadRef,
  trackLabelsRef,
  timelineRef,
  handleSelectionMouseDown,
  handleTimelineContentClick,
  handleSnapPointChange,
  clearSelectedElements,
  selectionBox,
  tracksContainerRef,
  currentSnapPoint,
  showSnapIndicator,
}: TimelineContentProps) {
  return (
    <div className="relative min-w-max min-h-max" style={{ minWidth: `${dynamicTimelineWidth}px` }}>
      <div className={`h-full grid grid-cols-[192px_1fr] ${tracks.length > 0 ? `grid-rows-[20px_repeat(${tracks.length},minmax(0,max-content))]` : 'grid-rows-[20px_1fr]'}`}>

        {/* Top-Left Corner (Empty space above track labels) */}
        <div className="sticky top-0 left-0 border-inset bg-card/[0.99]"></div>

        {/* Top Row (Sticky Ruler Header) */}
        <TimelineRuler
          duration={duration}
          zoomLevel={zoomLevel}
          currentTime={currentTime}
          seek={seek}
          rulerRef={rulerRef}
          rulerScrollRef={rulerScrollRef}
          tracksScrollRef={tracksScrollRef}
          playheadRef={playheadRef}
          handleSelectionMouseDown={handleSelectionMouseDown}
          handleTimelineContentClick={handleTimelineContentClick}
        />

        {/* Track Rows */}
        <TimelineTracksArea
          tracks={tracks}
          zoomLevel={zoomLevel}
          handleSnapPointChange={handleSnapPointChange}
          clearSelectedElements={clearSelectedElements}
        />
      </div>

      {/* Overlay Components - positioned absolutely relative to the timeline container */}
      <SelectionBox
        startPos={selectionBox?.startPos || null}
        currentPos={selectionBox?.currentPos || null}
        containerRef={tracksContainerRef}
        isActive={selectionBox?.isActive || false}
      />

      <TimelinePlayhead
        currentTime={currentTime}
        duration={duration}
        zoomLevel={zoomLevel}
        tracks={tracks}
        seek={seek}
        rulerRef={rulerRef}
        rulerScrollRef={rulerScrollRef}
        tracksScrollRef={tracksScrollRef}
        trackLabelsRef={trackLabelsRef}
        timelineRef={timelineRef}
        playheadRef={playheadRef}
        isSnappingToPlayhead={
          showSnapIndicator && currentSnapPoint?.type === "playhead"
        }
      />

      <SnapIndicator
        snapPoint={currentSnapPoint}
        zoomLevel={zoomLevel}
        tracks={tracks}
        timelineRef={timelineRef}
        trackLabelsRef={trackLabelsRef}
        isVisible={showSnapIndicator}
      />
    </div>
  );
}