@@ -5,15 +5,19 @@ import {
55 isTimelineRulerPress ,
66 getMarqueeRect ,
77 getTimelineClipRect ,
8+ getMarqueeClipCandidates ,
89 computeMarqueeSelection ,
910} from "./timelineMarquee" ;
11+ import { createTimelineClipIndex } from "../lib/timelineClipIndex" ;
12+ import type { TimelineElement } from "../store/playerStore" ;
1013import {
1114 GUTTER ,
1215 LANE_H ,
1316 TRACK_H ,
1417 RULER_H ,
1518 CLIP_Y ,
1619 TRACKS_LEFT_PAD ,
20+ createTimelineRowGeometry ,
1721 getTimelineRowTop ,
1822} from "./timelineLayout" ;
1923
@@ -95,9 +99,13 @@ describe("getMarqueeRect", () => {
9599
96100describe ( "getTimelineClipRect" , ( ) => {
97101 const trackOrder = [ 0 , 2 , 5 ] ;
102+ const geometry = createTimelineRowGeometry (
103+ trackOrder ,
104+ trackOrder . map ( ( ) => TRACK_H ) ,
105+ ) ;
98106
99107 it ( "maps start/duration to x via pps and the track row to y via the shared row→y helper" , ( ) => {
100- const rect = getTimelineClipRect ( { start : 2 , duration : 3 , track : 2 } , trackOrder , 100 , GUTTER ) ;
108+ const rect = getTimelineClipRect ( { start : 2 , duration : 3 , track : 2 } , geometry , 100 , GUTTER ) ;
101109 expect ( rect ) . toEqual ( {
102110 left : GUTTER + 200 ,
103111 top : getTimelineRowTop ( 1 ) + CLIP_Y ,
@@ -107,61 +115,59 @@ describe("getTimelineClipRect", () => {
107115 } ) ;
108116
109117 it ( "places the first visible track below the ruler + top breathing pad" , ( ) => {
110- const rect = getTimelineClipRect ( { start : 0 , duration : 1 , track : 0 } , trackOrder , 50 , GUTTER ) ;
118+ const rect = getTimelineClipRect ( { start : 0 , duration : 1 , track : 0 } , geometry , 50 , GUTTER ) ;
111119 expect ( rect ?. top ) . toBe ( getTimelineRowTop ( 0 ) + CLIP_Y ) ;
112120 expect ( rect ?. left ) . toBe ( GUTTER ) ;
113121 } ) ;
114122
115123 it ( "uses the row index in trackOrder, not the raw track number" , ( ) => {
116- const rect = getTimelineClipRect ( { start : 0 , duration : 1 , track : 5 } , trackOrder , 50 , GUTTER ) ;
124+ const rect = getTimelineClipRect ( { start : 0 , duration : 1 , track : 5 } , geometry , 50 , GUTTER ) ;
117125 expect ( rect ?. top ) . toBe ( getTimelineRowTop ( 2 ) + CLIP_Y ) ;
118126 } ) ;
119127
120128 it ( "uses cumulative tops and the resolved height for an expanded row" , ( ) => {
121129 const rowHeights = [ TRACK_H + 2 * LANE_H , TRACK_H , TRACK_H ] ;
130+ const expandedGeometry = createTimelineRowGeometry ( trackOrder , rowHeights ) ;
122131 const rect = getTimelineClipRect (
123132 { start : 0 , duration : 1 , track : 0 } ,
124- trackOrder ,
133+ expandedGeometry ,
125134 50 ,
126135 GUTTER ,
127- rowHeights ,
128136 ) ;
129137 expect ( rect ) . toMatchObject ( {
130138 top : getTimelineRowTop ( 0 , rowHeights ) + CLIP_Y ,
131- height : rowHeights [ 0 ] - CLIP_Y * 2 ,
139+ height : TRACK_H - CLIP_Y * 2 ,
132140 } ) ;
133141 expect (
134- getTimelineClipRect ( { start : 0 , duration : 1 , track : 2 } , trackOrder , 50 , GUTTER , rowHeights )
135- ?. top ,
142+ getTimelineClipRect ( { start : 0 , duration : 1 , track : 2 } , expandedGeometry , 50 , GUTTER ) ?. top ,
136143 ) . toBe ( getTimelineRowTop ( 1 , rowHeights ) + CLIP_Y ) ;
137144 } ) ;
138145
139146 it ( "enforces the 4px minimum rendered width" , ( ) => {
140- const rect = getTimelineClipRect (
141- { start : 0 , duration : 0.01 , track : 0 } ,
142- trackOrder ,
143- 10 ,
144- GUTTER ,
145- ) ;
147+ const rect = getTimelineClipRect ( { start : 0 , duration : 0.01 , track : 0 } , geometry , 10 , GUTTER ) ;
146148 expect ( rect ?. width ) . toBe ( 4 ) ;
147149 } ) ;
148150
149151 it ( "returns null for a track that is not displayed or an invalid pps" , ( ) => {
150152 expect (
151- getTimelineClipRect ( { start : 0 , duration : 1 , track : 9 } , trackOrder , 100 , GUTTER ) ,
153+ getTimelineClipRect ( { start : 0 , duration : 1 , track : 9 } , geometry , 100 , GUTTER ) ,
152154 ) . toBeNull ( ) ;
153155 expect (
154- getTimelineClipRect ( { start : 0 , duration : 1 , track : 0 } , trackOrder , 0 , GUTTER ) ,
156+ getTimelineClipRect ( { start : 0 , duration : 1 , track : 0 } , geometry , 0 , GUTTER ) ,
155157 ) . toBeNull ( ) ;
156158 expect (
157- getTimelineClipRect ( { start : 0 , duration : 1 , track : 0 } , trackOrder , NaN , GUTTER ) ,
159+ getTimelineClipRect ( { start : 0 , duration : 1 , track : 0 } , geometry , NaN , GUTTER ) ,
158160 ) . toBeNull ( ) ;
159161 } ) ;
160162} ) ;
161163
162164describe ( "computeMarqueeSelection" , ( ) => {
163165 // Two visible tracks: row 0 = track 0, row 1 = track 1. pps 100.
164166 const trackOrder = [ 0 , 1 ] ;
167+ const rowGeometry = createTimelineRowGeometry (
168+ trackOrder ,
169+ trackOrder . map ( ( ) => TRACK_H ) ,
170+ ) ;
165171 const pps = 100 ;
166172 const clips = [
167173 { id : "a" , start : 0 , duration : 1 , track : 0 } , // x [32,132], row 0
@@ -175,7 +181,7 @@ describe("computeMarqueeSelection", () => {
175181 const marquee = { left : ORIGIN , top : row0Top , width : 50 , height : 10 } ;
176182 const { ids, primaryId } = computeMarqueeSelection ( {
177183 clips,
178- trackOrder ,
184+ rowGeometry ,
179185 pps,
180186 contentOrigin : ORIGIN ,
181187 marquee,
@@ -188,7 +194,7 @@ describe("computeMarqueeSelection", () => {
188194 const marquee = { left : ORIGIN , top : row0Top , width : 60 , height : row1Top - row0Top + 5 } ;
189195 const { ids } = computeMarqueeSelection ( {
190196 clips,
191- trackOrder ,
197+ rowGeometry ,
192198 pps,
193199 contentOrigin : ORIGIN ,
194200 marquee,
@@ -200,7 +206,7 @@ describe("computeMarqueeSelection", () => {
200206 const marquee = { left : ORIGIN + 140 , top : row0Top , width : 50 , height : 10 } ;
201207 const { ids } = computeMarqueeSelection ( {
202208 clips,
203- trackOrder ,
209+ rowGeometry ,
204210 pps,
205211 contentOrigin : ORIGIN ,
206212 marquee,
@@ -212,7 +218,7 @@ describe("computeMarqueeSelection", () => {
212218 const marquee = { left : GUTTER + 140 , top : row0Top , width : 50 , height : 10 } ;
213219 const { ids, primaryId } = computeMarqueeSelection ( {
214220 clips,
215- trackOrder ,
221+ rowGeometry ,
216222 pps,
217223 contentOrigin : GUTTER ,
218224 marquee,
@@ -226,7 +232,7 @@ describe("computeMarqueeSelection", () => {
226232 const marquee = { left : GUTTER , top : row1Top , width : 100 , height : 10 } ;
227233 const { ids, primaryId } = computeMarqueeSelection ( {
228234 clips,
229- trackOrder ,
235+ rowGeometry ,
230236 pps,
231237 contentOrigin : GUTTER ,
232238 marquee,
@@ -240,10 +246,11 @@ describe("computeMarqueeSelection", () => {
240246 const wide = { left : ORIGIN , top : row0Top , width : 320 , height : 10 } ;
241247 const narrow = { left : ORIGIN , top : row0Top , width : 80 , height : 10 } ;
242248 expect (
243- computeMarqueeSelection ( { clips, trackOrder, pps, contentOrigin : ORIGIN , marquee : wide } ) . ids ,
249+ computeMarqueeSelection ( { clips, rowGeometry, pps, contentOrigin : ORIGIN , marquee : wide } )
250+ . ids ,
244251 ) . toEqual ( new Set ( [ "a" , "b" ] ) ) ;
245252 expect (
246- computeMarqueeSelection ( { clips, trackOrder , pps, contentOrigin : ORIGIN , marquee : narrow } )
253+ computeMarqueeSelection ( { clips, rowGeometry , pps, contentOrigin : ORIGIN , marquee : narrow } )
247254 . ids ,
248255 ) . toEqual ( new Set ( [ "a" ] ) ) ;
249256 } ) ;
@@ -252,11 +259,52 @@ describe("computeMarqueeSelection", () => {
252259 const marquee = { left : 0 , top : 0 , width : 10000 , height : 10000 } ;
253260 const { ids } = computeMarqueeSelection ( {
254261 clips : [ { id : "x" , start : 0 , duration : 1 , track : 7 } ] ,
255- trackOrder ,
262+ rowGeometry ,
256263 pps,
257264 contentOrigin : GUTTER ,
258265 marquee,
259266 } ) ;
260267 expect ( ids ) . toEqual ( new Set ( ) ) ;
261268 } ) ;
262269} ) ;
270+
271+ describe ( "getMarqueeClipCandidates" , ( ) => {
272+ it ( "queries only the intersecting rows and time span" , ( ) => {
273+ const rowGeometry = createTimelineRowGeometry ( [ 0 , 1 , 2 ] , [ TRACK_H , TRACK_H , TRACK_H ] ) ;
274+ const near : TimelineElement = { id : "near" , tag : "div" , start : 1 , duration : 1 , track : 1 } ;
275+ const wrongTime : TimelineElement = {
276+ id : "wrong-time" ,
277+ tag : "div" ,
278+ start : 20 ,
279+ duration : 1 ,
280+ track : 1 ,
281+ } ;
282+ const wrongRow : TimelineElement = {
283+ id : "wrong-row" ,
284+ tag : "div" ,
285+ start : 1 ,
286+ duration : 1 ,
287+ track : 2 ,
288+ } ;
289+ const clipIndex = createTimelineClipIndex ( [
290+ [ 0 , [ ] ] ,
291+ [ 1 , [ near , wrongTime ] ] ,
292+ [ 2 , [ wrongRow ] ] ,
293+ ] ) ;
294+
295+ expect (
296+ getMarqueeClipCandidates ( {
297+ clipIndex,
298+ rowGeometry,
299+ marquee : {
300+ left : ORIGIN + 100 ,
301+ top : getTimelineRowTop ( 1 ) ,
302+ width : 100 ,
303+ height : TRACK_H - 1 ,
304+ } ,
305+ pps : 100 ,
306+ contentOrigin : ORIGIN ,
307+ } ) ,
308+ ) . toEqual ( [ near ] ) ;
309+ } ) ;
310+ } ) ;
0 commit comments