2
2
3
3
class TimelineEngine {
4
4
/**
5
+ * @param {Theme } theme
5
6
* @param {Renderer } renderer
6
7
* @param {Legend } legend
7
8
* @param {Element } threshold
8
9
* @param {Object } request
9
10
* @param {Number } eventHeight
10
11
* @param {Number } horizontalMargin
11
12
*/
12
- constructor ( renderer , legend , threshold , request , eventHeight = 36 , horizontalMargin = 10 ) {
13
+ constructor ( theme , renderer , legend , threshold , request , eventHeight = 36 , horizontalMargin = 10 ) {
14
+ this . theme = theme ;
13
15
this . renderer = renderer ;
14
16
this . legend = legend ;
15
17
this . threshold = threshold ;
@@ -81,7 +83,7 @@ class TimelineEngine {
81
83
const lines = periods . map ( period => this . createPeriod ( period , category ) ) ;
82
84
const label = this . createLabel ( this . getShortName ( name ) , duration , memory , periods [ 0 ] ) ;
83
85
const title = this . renderer . createTitle ( name ) ;
84
- const group = this . renderer . group ( [ title , border , label ] . concat ( lines ) , this . legend . getClassname ( event . category ) ) ;
86
+ const group = this . renderer . group ( [ title , border , label ] . concat ( lines ) , this . theme . getCategoryColor ( event . category ) ) ;
85
87
86
88
event . elements = Object . assign ( event . elements || { } , { group, label, border } ) ;
87
89
@@ -100,7 +102,7 @@ class TimelineEngine {
100
102
}
101
103
102
104
createPeriod ( period , category ) {
103
- const timeline = this . renderer . createPath ( null , 'timeline-period' ) ;
105
+ const timeline = this . renderer . createPath ( null , 'timeline-period' , this . theme . getCategoryColor ( category ) ) ;
104
106
105
107
period . draw = category === 'section' ? this . renderer . setSectionLine : this . renderer . setPeriodLine ;
106
108
period . elements = Object . assign ( period . elements || { } , { timeline } ) ;
@@ -213,14 +215,14 @@ class TimelineEngine {
213
215
}
214
216
215
217
class Legend {
216
- constructor ( element , classnames ) {
218
+ constructor ( element , theme ) {
217
219
this . element = element ;
218
- this . classnames = classnames ;
220
+ this . theme = theme ;
219
221
220
222
this . toggle = this . toggle . bind ( this ) ;
221
223
this . createCategory = this . createCategory . bind ( this ) ;
222
224
223
- this . categories = Array . from ( Object . keys ( classnames ) ) . map ( this . createCategory ) ;
225
+ this . categories = Array . from ( this . theme . getDefaultCategories ( ) ) . map ( this . createCategory ) ;
224
226
}
225
227
226
228
add ( category ) {
@@ -229,8 +231,8 @@ class Legend {
229
231
230
232
createCategory ( category ) {
231
233
const element = document . createElement ( 'button' ) ;
232
-
233
- element . className = `timeline-category ${ this . getClassname ( category ) } active` ;
234
+ element . className = `timeline-category active` ;
235
+ element . style . borderColor = this . theme . getCategoryColor ( category ) ;
234
236
element . innerText = category ;
235
237
element . value = category ;
236
238
element . type = 'button' ;
@@ -390,13 +392,17 @@ class SvgRenderer {
390
392
return element ;
391
393
}
392
394
393
- createPath ( path = null , className = null ) {
395
+ createPath ( path = null , className = null , color = null ) {
394
396
const element = this . create ( 'path' , className ) ;
395
397
396
398
if ( path ) {
397
399
element . setAttribute ( 'd' , path ) ;
398
400
}
399
401
402
+ if ( color ) {
403
+ element . setAttribute ( 'fill' , color ) ;
404
+ }
405
+
400
406
return element ;
401
407
}
402
408
@@ -410,3 +416,55 @@ class SvgRenderer {
410
416
return element ;
411
417
}
412
418
}
419
+
420
+ class Theme {
421
+ constructor ( element ) {
422
+ this . reservedCategoryColors = {
423
+ 'default' : '#777' ,
424
+ 'section' : '#999' ,
425
+ 'event_listener' : '#00b8f5' ,
426
+ 'template' : '#66cc00' ,
427
+ 'doctrine' : '#ff6633' ,
428
+ 'messenger_middleware' : '#bdb81e' ,
429
+ 'controller.argument_value_resolver' : '#8c5de6' ,
430
+ } ;
431
+
432
+ this . customCategoryColors = [
433
+ '#dbab09' , // dark yellow
434
+ '#ea4aaa' , // pink
435
+ '#964b00' , // brown
436
+ '#22863a' , // dark green
437
+ '#0366d6' , // dark blue
438
+ '#17a2b8' , // teal
439
+ ] ;
440
+
441
+ this . getCategoryColor = this . getCategoryColor . bind ( this ) ;
442
+ this . getDefaultCategories = this . getDefaultCategories . bind ( this ) ;
443
+ }
444
+
445
+ getDefaultCategories ( ) {
446
+ return Object . keys ( this . reservedCategoryColors ) ;
447
+ }
448
+
449
+ getCategoryColor ( category ) {
450
+ return this . reservedCategoryColors [ category ] || this . getRandomColor ( category ) ;
451
+ }
452
+
453
+ getRandomColor ( category ) {
454
+ // instead of pure randomness, colors are assigned deterministically based on the
455
+ // category name, to ensure that each custom category always displays the same color
456
+ return this . customCategoryColors [ this . hash ( category ) % this . customCategoryColors . length ] ;
457
+ }
458
+
459
+ // copied from https://github.com/darkskyapp/string-hash
460
+ hash ( string ) {
461
+ var hash = 5381 ;
462
+ var i = string . length ;
463
+
464
+ while ( i ) {
465
+ hash = ( hash * 33 ) ^ string . charCodeAt ( -- i ) ;
466
+ }
467
+
468
+ return hash >>> 0 ;
469
+ }
470
+ }
0 commit comments