@@ -63,6 +63,19 @@ module.exports = function draw(gd) {
63
63
. call ( Color . fill , opts . bgcolor )
64
64
. style ( 'stroke-width' , opts . borderwidth + 'px' ) ;
65
65
66
+
67
+ var title = fullLayout . legend . title ;
68
+ gd . _fullLayout . _legendTitleWidth = 0 ;
69
+ gd . _fullLayout . _legendTitleHeight = 0 ;
70
+ if ( title . text ) {
71
+ var titleEl = Lib . ensureSingle ( legend , 'text' , 'legendtitletext' ) ;
72
+ titleEl . attr ( 'text-anchor' , 'start' )
73
+ . classed ( 'user-select-none' , true )
74
+ . call ( Drawing . font , title . font )
75
+ . text ( title . text ) ;
76
+
77
+ textLayout ( titleEl , legend , gd ) ; // handle mathjax or multi-line text and compute title height
78
+ }
66
79
var scrollBox = Lib . ensureSingle ( legend , 'g' , 'scrollbox' ) ;
67
80
68
81
var scrollBar = Lib . ensureSingle ( legend , 'rect' , 'scrollbar' , function ( s ) {
@@ -121,7 +134,7 @@ module.exports = function draw(gd) {
121
134
}
122
135
123
136
// Set size and position of all the elements that make up a legend:
124
- // legend, background and border, scroll box and scroll bar
137
+ // legend, background and border, scroll box and scroll bar as well as title
125
138
Drawing . setTranslate ( legend , lx , ly ) ;
126
139
127
140
// to be safe, remove previous listeners
@@ -375,18 +388,12 @@ function drawTexts(g, gd) {
375
388
376
389
svgTextUtils . positionText ( textEl , constants . textGap , 0 ) ;
377
390
378
- function textLayout ( s ) {
379
- svgTextUtils . convertToTspans ( s , gd , function ( ) {
380
- computeTextDimensions ( g , gd ) ;
381
- } ) ;
382
- }
383
-
384
391
if ( isEditable ) {
385
392
textEl . call ( svgTextUtils . makeEditable , { gd : gd , text : name } )
386
- . call ( textLayout )
393
+ . call ( textLayout , g , gd )
387
394
. on ( 'edit' , function ( newName ) {
388
395
this . text ( ensureLength ( newName , maxNameLength ) )
389
- . call ( textLayout ) ;
396
+ . call ( textLayout , g , gd ) ;
390
397
391
398
var fullInput = legendItem . trace . _fullInput || { } ;
392
399
var update = { } ;
@@ -407,7 +414,7 @@ function drawTexts(g, gd) {
407
414
return Registry . call ( '_guiRestyle' , gd , update , traceIndex ) ;
408
415
} ) ;
409
416
} else {
410
- textLayout ( textEl ) ;
417
+ textLayout ( textEl , g , gd ) ;
411
418
}
412
419
}
413
420
@@ -460,17 +467,25 @@ function setupTraceToggle(g, gd) {
460
467
} ) ;
461
468
}
462
469
470
+ function textLayout ( s , g , gd ) {
471
+ svgTextUtils . convertToTspans ( s , gd , function ( ) {
472
+ computeTextDimensions ( g , gd ) ;
473
+ } ) ;
474
+ }
475
+
463
476
function computeTextDimensions ( g , gd ) {
464
477
var legendItem = g . data ( ) [ 0 ] [ 0 ] ;
465
-
466
- if ( ! legendItem . trace . showlegend ) {
478
+ if ( legendItem && ! legendItem . trace . showlegend ) {
467
479
g . remove ( ) ;
468
480
return ;
469
481
}
470
482
471
483
var mathjaxGroup = g . select ( 'g[class*=math-group]' ) ;
472
484
var mathjaxNode = mathjaxGroup . node ( ) ;
473
- var opts = gd . _fullLayout . legend ;
485
+ var bw = gd . _fullLayout . legend . borderwidth ;
486
+ var opts = legendItem ?
487
+ gd . _fullLayout . legend :
488
+ gd . _fullLayout . legend . title ;
474
489
var lineHeight = opts . font . size * LINE_SPACING ;
475
490
var height , width ;
476
491
@@ -480,24 +495,43 @@ function computeTextDimensions(g, gd) {
480
495
height = mathjaxBB . height ;
481
496
width = mathjaxBB . width ;
482
497
483
- Drawing . setTranslate ( mathjaxGroup , 0 , ( height / 4 ) ) ;
498
+ if ( legendItem ) {
499
+ Drawing . setTranslate ( mathjaxGroup , 0 , height * 0.25 ) ;
500
+ } else { // case of title
501
+ Drawing . setTranslate ( mathjaxGroup , bw , height * 0.75 + bw ) ;
502
+ }
484
503
} else {
485
- var text = g . select ( '.legendtext' ) ;
486
- var textLines = svgTextUtils . lineCount ( text ) ;
487
- var textNode = text . node ( ) ;
504
+ var textEl = g . select ( legendItem ?
505
+ '.legendtext' : '.legendtitletext'
506
+ ) ;
507
+ var textLines = svgTextUtils . lineCount ( textEl ) ;
508
+ var textNode = textEl . node ( ) ;
488
509
489
510
height = lineHeight * textLines ;
490
511
width = textNode ? Drawing . bBox ( textNode ) . width : 0 ;
491
512
492
513
// approximation to height offset to center the font
493
514
// to avoid getBoundingClientRect
494
- var textY = lineHeight * ( 0.3 + ( 1 - textLines ) / 2 ) ;
495
- svgTextUtils . positionText ( text , constants . textGap , textY ) ;
515
+ var textY = lineHeight * ( ( textLines - 1 ) / 2 - 0.3 ) ;
516
+ if ( legendItem ) {
517
+ svgTextUtils . positionText ( textEl , constants . textGap , - textY ) ;
518
+ } else { // case of title
519
+ svgTextUtils . positionText ( textEl , constants . titlePad + bw , lineHeight + bw ) ;
520
+ }
496
521
}
497
522
498
- legendItem . lineHeight = lineHeight ;
499
- legendItem . height = Math . max ( height , 16 ) + 3 ;
500
- legendItem . width = width ;
523
+ if ( legendItem ) {
524
+ legendItem . lineHeight = lineHeight ;
525
+ legendItem . height = Math . max ( height , 16 ) + 3 ;
526
+ legendItem . width = width ;
527
+ } else { // case of title
528
+ if ( opts . side . indexOf ( 'left' ) !== - 1 ) {
529
+ gd . _fullLayout . _legendTitleWidth = width ;
530
+ }
531
+ if ( opts . side . indexOf ( 'top' ) !== - 1 ) {
532
+ gd . _fullLayout . _legendTitleHeight = height ;
533
+ }
534
+ }
501
535
}
502
536
503
537
/*
@@ -514,6 +548,9 @@ function computeLegendDimensions(gd, groups, traces) {
514
548
var fullLayout = gd . _fullLayout ;
515
549
var opts = fullLayout . legend ;
516
550
var gs = fullLayout . _size ;
551
+ opts . _titleWidth = fullLayout . _legendTitleWidth ;
552
+ opts . _titleHeight = fullLayout . _legendTitleHeight ;
553
+
517
554
var isVertical = helpers . isVertical ( opts ) ;
518
555
var isGrouped = helpers . isGrouped ( opts ) ;
519
556
@@ -541,7 +578,10 @@ function computeLegendDimensions(gd, groups, traces) {
541
578
if ( isVertical ) {
542
579
traces . each ( function ( d ) {
543
580
var h = d [ 0 ] . height ;
544
- Drawing . setTranslate ( this , bw , itemGap + bw + opts . _height + h / 2 ) ;
581
+ Drawing . setTranslate ( this ,
582
+ bw + opts . _titleWidth ,
583
+ bw + opts . _titleHeight + opts . _height + h / 2 + itemGap
584
+ ) ;
545
585
opts . _height += h ;
546
586
opts . _width = Math . max ( opts . _width , d [ 0 ] . width ) ;
547
587
} ) ;
@@ -591,7 +631,10 @@ function computeLegendDimensions(gd, groups, traces) {
591
631
var offsetY = 0 ;
592
632
d3 . select ( this ) . selectAll ( 'g.traces' ) . each ( function ( d ) {
593
633
var h = d [ 0 ] . height ;
594
- Drawing . setTranslate ( this , 0 , itemGap + bw + h / 2 + offsetY ) ;
634
+ Drawing . setTranslate ( this ,
635
+ opts . _titleWidth ,
636
+ bw + opts . _titleHeight + itemGap + h / 2 + offsetY
637
+ ) ;
595
638
offsetY += h ;
596
639
maxWidthInGroup = Math . max ( maxWidthInGroup , textGap + d [ 0 ] . width ) ;
597
640
} ) ;
@@ -634,7 +677,10 @@ function computeLegendDimensions(gd, groups, traces) {
634
677
maxItemHeightInRow = 0 ;
635
678
}
636
679
637
- Drawing . setTranslate ( this , bw + offsetX , itemGap + bw + h / 2 + offsetY ) ;
680
+ Drawing . setTranslate ( this ,
681
+ bw + opts . _titleWidth + offsetX ,
682
+ bw + opts . _titleHeight + offsetY + h / 2 + itemGap
683
+ ) ;
638
684
639
685
rowWidth = offsetX + w + itemGap ;
640
686
offsetX += next ;
@@ -651,8 +697,8 @@ function computeLegendDimensions(gd, groups, traces) {
651
697
}
652
698
}
653
699
654
- opts . _width = Math . ceil ( opts . _width ) ;
655
- opts . _height = Math . ceil ( opts . _height ) ;
700
+ opts . _width = Math . ceil ( opts . _width + opts . _titleWidth ) ;
701
+ opts . _height = Math . ceil ( opts . _height + opts . _titleHeight ) ;
656
702
657
703
opts . _effHeight = Math . min ( opts . _height , opts . _maxHeight ) ;
658
704
0 commit comments