11import { debounce } from "lodash" ;
2- import React , { useState , useRef , useEffect , RefObject } from "react" ;
2+ import React , { useState , useRef , useEffect , RefObject , useLayoutEffect } from "react" ;
33
44import Draggable , { DraggableEventHandler } from "react-draggable" ;
55
@@ -42,6 +42,7 @@ import {
4242 selectActiveSegmentIndex as chapterSelectActiveSegmentIndex ,
4343 moveCut as chapterMoveCut ,
4444} from "../redux/chapterSlice" ;
45+ import TimelineStamps from "./TimelineStamps" ;
4546
4647/**
4748 * A container for visualizing the cutting of the video, as well as for controlling
@@ -81,11 +82,16 @@ const Timeline: React.FC<{
8182 const { ref, width = 1 } = useResizeObserver < HTMLDivElement > ( ) ;
8283 const scrollContainerRef = useRef < HTMLElement > ( null ) ;
8384 const { width : scrollContainerWidth = 1 } = useResizeObserver < HTMLElement > ( { ref : scrollContainerRef } ) ;
84- const topOffset = 20 ;
8585
8686 const currentlyScrolling = useRef ( false ) ;
8787 const zoomCenter = useRef ( 0 ) ;
8888
89+ // Vars for timelineStamps
90+ const timelineStampsHeight = 20 ;
91+ const waveformHeight = timelineHeight - timelineStampsHeight ;
92+ const [ scrollLeft , setScrollLeft ] = useState ( 0 ) ;
93+ const [ visibleWidth , setVisibleWidth ] = useState ( 0 ) ;
94+
8995 const updateScroll = ( ) => {
9096 if ( currentlyScrolling . current ) {
9197 currentlyScrolling . current = false ;
@@ -98,6 +104,16 @@ const Timeline: React.FC<{
98104 const scrubberVisible = scrollLeft <= scrubberPosition && scrubberPosition <= scrollLeft + clientWidth ;
99105
100106 zoomCenter . current = ( scrubberVisible ? scrubberPosition : centerPosition ) / width ;
107+
108+ } ;
109+
110+ const updateScrollMetrics = ( ) => {
111+ if ( ! scrollContainerRef . current ) {
112+ return ;
113+ }
114+ const el = scrollContainerRef . current ;
115+ setScrollLeft ( el . scrollLeft ) ;
116+ setVisibleWidth ( el . clientWidth ) ;
101117 } ;
102118
103119 const displayPercentage = ( durationInSeconds / displayDuration ) ;
@@ -106,6 +122,11 @@ const Timeline: React.FC<{
106122 } ;
107123 const zoomedWidth = getWaveformWidth ( scrollContainerWidth ) ;
108124
125+ // Make sure visibleWidth is set so canvas is drawn on first render
126+ useLayoutEffect ( ( ) => {
127+ updateScrollMetrics ( ) ;
128+ } , [ width , duration ] ) ;
129+
109130 // eslint-disable-next-line react-hooks/exhaustive-deps
110131 useEffect ( updateScroll , [ currentlyAt , timelineZoom , width , scrollContainerWidth ] ) ;
111132
@@ -124,7 +145,6 @@ const Timeline: React.FC<{
124145 position : "relative" , // Need to set position for Draggable bounds to work
125146 height : timelineHeight + "px" ,
126147 width : `${ zoomedWidth } px` , // Width modified by zoom
127- top : `${ topOffset } px` ,
128148 } ) ;
129149
130150 // Update the current time based on the position clicked on the timeline
@@ -136,17 +156,31 @@ const Timeline: React.FC<{
136156 } ;
137157
138158 return (
139- < ScrollContainer
140- innerRef = { scrollContainerRef }
141- css = { { overflowY : "hidden" , width : "100%" , height : `${ timelineHeight + topOffset } px` } }
142- vertical = { false }
143- horizontal = { true }
144- // dom elements with this id in the container will not trigger scrolling when dragged
145- ignoreElements = { ".prevent-drag-scroll" }
146- hideScrollbars = { false } // ScrollContainer hides scrollbars per default
147- onEndScroll = { updateScroll }
148- >
149- < CuttingActionsContextMenu >
159+ < CuttingActionsContextMenu >
160+ < div css = { css ( { position : "absolute" } ) } >
161+ < TimelineStamps
162+ durationMs = { duration }
163+ zoomedWidth = { zoomedWidth }
164+ scrollLeft = { scrollLeft }
165+ visibleWidth = { visibleWidth }
166+ height = { timelineStampsHeight }
167+ />
168+ </ div >
169+ < ScrollContainer
170+ innerRef = { scrollContainerRef }
171+ css = { {
172+ overflowY : "hidden" ,
173+ width : "100%" ,
174+ height : `${ timelineHeight } px` ,
175+ } }
176+ vertical = { false }
177+ horizontal = { true }
178+ // dom elements with this id in the container will not trigger scrolling when dragged
179+ ignoreElements = { ".prevent-drag-scroll" }
180+ hideScrollbars = { false } // ScrollContainer hides scrollbars per default
181+ onScroll = { updateScrollMetrics }
182+ onEndScroll = { updateScroll }
183+ >
150184 < div ref = { ref } css = { timelineStyle } onMouseDown = { e => setCurrentlyAtToClick ( e ) } >
151185 < Scrubber
152186 ref = { scrubberRef }
@@ -157,15 +191,15 @@ const Timeline: React.FC<{
157191 setCurrentlyAt = { setCurrentlyAt }
158192 setIsPlaying = { setIsPlaying }
159193 />
160- < div css = { { position : "relative" , height : timelineHeight + "px" } } >
194+ < div css = { { position : "relative" , height : timelineHeight + "px" , top : ` ${ timelineStampsHeight } px` } } >
161195 < Waveforms
162- timelineHeight = { ! isChapters ? timelineHeight : ( timelineHeight / 4 ) * 3 }
163- topOffset = { ! isChapters ? undefined : ( timelineHeight / 4 ) * 1 }
196+ timelineHeight = { ! isChapters ? waveformHeight : ( waveformHeight / 4 ) * 3 }
197+ topOffset = { ! isChapters ? undefined : ( waveformHeight / 4 ) * 1 }
164198 />
165199 { isChapters &&
166200 < SegmentsList
167201 timelineWidth = { width }
168- timelineHeight = { ( timelineHeight / 4 ) * 1 }
202+ timelineHeight = { ( waveformHeight / 4 ) * 1 }
169203 styleByActiveSegment = { styleByActiveSegment }
170204 tabable = { true }
171205 selectSegments = { chapterSelectSegments }
@@ -175,7 +209,7 @@ const Timeline: React.FC<{
175209 }
176210 < SegmentsList
177211 timelineWidth = { width }
178- timelineHeight = { ! isChapters ? timelineHeight : ( timelineHeight / 4 ) * 3 }
212+ timelineHeight = { ! isChapters ? waveformHeight : ( waveformHeight / 4 ) * 3 }
179213 styleByActiveSegment = { ! isChapters ? styleByActiveSegment : false }
180214 tabable = { true }
181215 selectSegments = { selectSegments }
@@ -184,8 +218,8 @@ const Timeline: React.FC<{
184218 />
185219 </ div >
186220 </ div >
187- </ CuttingActionsContextMenu >
188- </ ScrollContainer >
221+ </ ScrollContainer >
222+ </ CuttingActionsContextMenu >
189223 ) ;
190224} ;
191225
@@ -310,12 +344,11 @@ export const Scrubber = React.forwardRef<HTMLDivElement, ScrubberProps>((props,
310344 height : timelineHeight + 20 + "px" , // TODO: CHECK IF height: "100%",
311345 width : "1px" ,
312346 position : "absolute" ,
313- zIndex : 2 ,
347+ zIndex : 20 ,
314348 display : "flex" ,
315349 flexDirection : "column" ,
316350 justifyContent : "space-between" ,
317351 alignItems : "center" ,
318- top : "-20px" ,
319352 } ) ;
320353
321354 const scrubberDragHandleStyle = css ( {
0 commit comments