@@ -9,10 +9,11 @@ import {
99import { useReaper } from "../../Data/Context" ;
1010import {
1111 type CurrentTime ,
12- type ParsedMeta ,
12+ type Marker ,
1313 PlayState ,
1414 type Region ,
1515 type RegionMeta ,
16+ type RegionsMeta ,
1617} from "../../Data/State" ;
1718import { Icons } from "../UI/Icons" ;
1819import { Toggle } from "../UI/Toggle" ;
@@ -41,11 +42,6 @@ function time(totalSeconds: number): string {
4142function Progress ( p : { region : Region ; currentTime : CurrentTime } ) {
4243 return (
4344 < >
44- < div
45- class = "absolute top-0 right-0 bottom-0 left-0 bg-black opacity-40"
46- style = { { width : `${ progress ( p . region , p . currentTime . seconds ) } %` } }
47- />
48-
4945 < div class = "absolute top-0 right-0 bottom-0 flex items-center px-1 opacity-90" >
5046 < p class = "rounded-lg bg-neutral-900/40 p-1 font-mono text-gray-300 text-xs" >
5147 { time ( p . currentTime . seconds - p . region . startTime ) } /{ " " }
@@ -64,7 +60,10 @@ function filterRegions(regions: RegionWithMeta[]): Region[] {
6460 return regions . filter ( ( r ) => ! ( r . meta ?. disabled ?? false ) ) ;
6561}
6662
67- function prepareRegions ( regions : Region [ ] , meta : ParsedMeta ) : RegionWithMeta [ ] {
63+ function prepareRegions (
64+ regions : Region [ ] ,
65+ meta : RegionsMeta ,
66+ ) : RegionWithMeta [ ] {
6867 const result = regions . map ( ( r ) => ( { ...r , meta : meta [ r . id ] } ) ) ;
6968 result . sort (
7069 ( a , b ) => ( a . meta ?. index ?? 10000 + a . id ) - ( b . meta ?. index ?? 10000 + b . id ) ,
@@ -84,10 +83,37 @@ function playStateClass(playState: PlayState): string {
8483 }
8584}
8685
86+ function markerPosition ( region : Region , marker : Marker ) : number {
87+ return (
88+ ( ( marker . startTime - region . startTime ) /
89+ ( region . endTime - region . startTime ) ) *
90+ 100
91+ ) ;
92+ }
93+
94+ function textColor ( color ?: string ) : "text-white" | "text-black" {
95+ if ( color == null ) {
96+ return "text-white" ;
97+ }
98+
99+ const squareDist =
100+ color
101+ . match ( / ^ # ( ..) ( ..) ( ..) $ / )
102+ ?. slice ( 1 )
103+ ?. map ( ( c ) => Number . parseInt ( c , 16 ) ** 2 )
104+ ?. reduce ( ( acc , c ) => acc + c , 0 ) ?? 0 ;
105+
106+ if ( squareDist > ( 255 * 255 * 3 ) / 2 ) {
107+ return "text-black" ;
108+ }
109+
110+ return "text-white" ;
111+ }
112+
87113function RegionList ( ) {
88114 const {
89- actions : { moveToRegion } ,
90- data : { playState, currentTime, regions, regionMeta } ,
115+ actions : { moveToRegion, moveToMarker } ,
116+ data : { playState, currentTime, regions, regionMeta, regionMarkers } ,
91117 } = useReaper ( ) ;
92118
93119 const isPlaying = createSelector (
@@ -101,36 +127,88 @@ function RegionList() {
101127 ) ;
102128
103129 return (
104- < div class = "flex flex-col" >
105- < For each = { processedRegions ( ) } >
106- { ( region ) => (
107- < button
108- type = "button"
109- class = { `btn-outlined relative my-1 flex h-10 grow overflow-clip px-3 hover:brightness-125 ${
110- isPlaying ( region ) && `selected ${ playStateClass ( playState ( ) ) } `
111- } `}
112- style = {
113- region . color != null ? { "background-color" : region . color } : { }
114- }
115- onClick = { ( ) => moveToRegion ( region ) }
116- >
117- { isPlaying ( region ) && (
118- < Progress region = { region } currentTime = { currentTime ( ) } />
119- ) }
120-
121- < div class = "absolute top-1 bottom-0 left-0.5 opacity-90" >
122- < div class = "rounded-lg bg-neutral-900/40 p-1 font-normal text-base text-gray-200" >
123- { region . id } . { region . name }
130+ < div >
131+ < div
132+ class = "flex justify-center rounded-md shadow-sm h-10 mb-4"
133+ role = "group"
134+ >
135+ < button
136+ class = "flex items-center btn-primary border rounded-none rounded-l basis-1/2"
137+ type = "button"
138+ onclick = { ( ) => moveToMarker ( "previous" ) }
139+ >
140+ < Icons . Left />
141+ < span class = "flex-grow" > Previous marker</ span >
142+ </ button >
143+ < button
144+ class = "flex items-center btn-primary border border-l-0 rounded-none rounded-r basis-1/2"
145+ type = "button"
146+ onclick = { ( ) => moveToMarker ( "next" ) }
147+ >
148+ < span class = "flex-grow" > Next marker</ span >
149+ < Icons . Right />
150+ </ button >
151+ </ div >
152+
153+ < div class = "flex flex-col" >
154+ < For each = { processedRegions ( ) } >
155+ { ( region ) => (
156+ < button
157+ type = "button"
158+ class = { `btn-outlined relative my-1 flex h-10 grow overflow-clip px-3 hover:brightness-125 ${
159+ isPlaying ( region ) && `selected ${ playStateClass ( playState ( ) ) } `
160+ } `}
161+ style = {
162+ region . color != null ? { "background-color" : region . color } : { }
163+ }
164+ onClick = { ( ) => moveToRegion ( region ) }
165+ >
166+ { isPlaying ( region ) && (
167+ < div
168+ class = "absolute top-0 right-0 bottom-0 left-0 bg-black opacity-40"
169+ style = { {
170+ width : `${ progress ( region , currentTime ( ) . seconds ) } %` ,
171+ } }
172+ />
173+ ) }
174+
175+ < For each = { regionMarkers ( ) [ region . id ] ?? [ ] } >
176+ { ( marker ) => (
177+ < div
178+ class = "absolute top-0 bottom-0 border-l border-solid border-red-600 opacity-50"
179+ style = { {
180+ left : `${ markerPosition ( region , marker ) } %` ,
181+ "border-color" : marker . color ,
182+ } }
183+ >
184+ < div
185+ class = { `absolute top-[-3px] text-xs bg-red-600 px-1 rounded-br-md ${ textColor (
186+ marker . color ,
187+ ) } `}
188+ style = { { "background-color" : marker . color } }
189+ >
190+ { marker . id }
191+ </ div >
192+ </ div >
193+ ) }
194+ </ For >
195+ { isPlaying ( region ) && (
196+ < Progress region = { region } currentTime = { currentTime ( ) } />
197+ ) }
198+ < div class = "absolute top-0 bottom-0 left-0.5 opacity-90 flex justify-center items-center" >
199+ < div class = "rounded-lg bg-neutral-900/40 p-1 font-normal text-base text-gray-200" >
200+ { region . id } . { region . name }
201+ </ div >
124202 </ div >
125- </ div >
126- </ button >
127- ) }
128- </ For >
203+ </ button >
204+ ) }
205+ </ For >
206+ </ div >
129207 </ div >
130208 ) ;
131209}
132210
133- function moveUp ( regions : RegionWithMeta [ ] , index : number ) : ParsedMeta {
211+ function moveUp ( regions : RegionWithMeta [ ] , index : number ) : RegionsMeta {
134212 const newRegions = [ ...regions ] ;
135213
136214 const temp = newRegions [ index ] ;
@@ -145,10 +223,10 @@ function moveUp(regions: RegionWithMeta[], index: number): ParsedMeta {
145223 } ;
146224
147225 return acc ;
148- } , { } as ParsedMeta ) ;
226+ } , { } as RegionsMeta ) ;
149227}
150228
151- function moveDown ( regions : RegionWithMeta [ ] , index : number ) : ParsedMeta {
229+ function moveDown ( regions : RegionWithMeta [ ] , index : number ) : RegionsMeta {
152230 const newRegions = [ ...regions ] ;
153231
154232 const temp = newRegions [ index ] ;
@@ -163,10 +241,10 @@ function moveDown(regions: RegionWithMeta[], index: number): ParsedMeta {
163241 } ;
164242
165243 return acc ;
166- } , { } as ParsedMeta ) ;
244+ } , { } as RegionsMeta ) ;
167245}
168246
169- function toggleEnabled ( regions : RegionWithMeta [ ] , index : number ) : ParsedMeta {
247+ function toggleEnabled ( regions : RegionWithMeta [ ] , index : number ) : RegionsMeta {
170248 const parsedMeta = regions . reduce ( ( acc , r , i ) => {
171249 acc [ r . id ] = {
172250 id : r . id ,
@@ -175,7 +253,7 @@ function toggleEnabled(regions: RegionWithMeta[], index: number): ParsedMeta {
175253 } ;
176254
177255 return acc ;
178- } , { } as ParsedMeta ) ;
256+ } , { } as RegionsMeta ) ;
179257
180258 parsedMeta [ regions [ index ] . id ] . disabled = ! (
181259 regions [ index ] . meta ?. disabled ?? false
0 commit comments