@@ -5,6 +5,7 @@ var Registry = require('../registry');
55var Plots = require ( '../plots/plots' ) ;
66
77var Lib = require ( '../lib' ) ;
8+ var svgTextUtils = require ( '../lib/svg_text_utils' ) ;
89var clearGlCanvases = require ( '../lib/clear_gl_canvases' ) ;
910
1011var Color = require ( '../components/color' ) ;
@@ -397,24 +398,120 @@ function findCounterAxisLineWidth(ax, side, counterAx, axList) {
397398}
398399
399400exports . drawMainTitle = function ( gd ) {
401+ var title = gd . _fullLayout . title ;
400402 var fullLayout = gd . _fullLayout ;
401-
402403 var textAnchor = getMainTitleTextAnchor ( fullLayout ) ;
403404 var dy = getMainTitleDy ( fullLayout ) ;
405+ var y = getMainTitleY ( fullLayout , dy ) ;
406+ var x = getMainTitleX ( fullLayout , textAnchor ) ;
404407
405408 Titles . draw ( gd , 'gtitle' , {
406409 propContainer : fullLayout ,
407410 propName : 'title.text' ,
408411 placeholder : fullLayout . _dfltTitle . plot ,
409- attributes : {
410- x : getMainTitleX ( fullLayout , textAnchor ) ,
411- y : getMainTitleY ( fullLayout , dy ) ,
412+ attributes : ( {
413+ x : x ,
414+ y : y ,
412415 'text-anchor' : textAnchor ,
413416 dy : dy
414- }
417+ } )
415418 } ) ;
419+
420+ if ( title . text && title . automargin ) {
421+ var titleObj = d3 . selectAll ( '.gtitle' ) ;
422+ var titleHeight = Drawing . bBox ( titleObj . node ( ) ) . height ;
423+ var pushMargin = needsMarginPush ( gd , title , titleHeight ) ;
424+ if ( pushMargin > 0 ) {
425+ applyTitleAutoMargin ( gd , y , pushMargin , titleHeight ) ;
426+ // Re-position the title once we know where it needs to be
427+ titleObj . attr ( {
428+ x : x ,
429+ y : y ,
430+ 'text-anchor' : textAnchor ,
431+ dy : getMainTitleDyAdj ( title . yanchor )
432+ } ) . call ( svgTextUtils . positionText , x , y ) ;
433+ }
434+ }
416435} ;
417436
437+
438+ function isOutsideContainer ( gd , title , position , y , titleHeight ) {
439+ var plotHeight = title . yref === 'paper' ? gd . _fullLayout . _size . h : gd . _fullLayout . height ;
440+ var yPosTop = Lib . isTopAnchor ( title ) ? y : y - titleHeight ; // Standardize to the top of the title
441+ var yPosRel = position === 'b' ? plotHeight - yPosTop : yPosTop ; // Position relative to the top or bottom of plot
442+ if ( ( Lib . isTopAnchor ( title ) && position === 't' ) || Lib . isBottomAnchor ( title ) && position === 'b' ) {
443+ return false ;
444+ } else {
445+ return yPosRel < titleHeight ;
446+ }
447+ }
448+
449+ function containerPushVal ( position , titleY , titleYanchor , height , titleDepth ) {
450+ var push = 0 ;
451+ if ( titleYanchor === 'middle' ) {
452+ push += titleDepth / 2 ;
453+ }
454+ if ( position === 't' ) {
455+ if ( titleYanchor === 'top' ) {
456+ push += titleDepth ;
457+ }
458+ push += ( height - titleY * height ) ;
459+ } else {
460+ if ( titleYanchor === 'bottom' ) {
461+ push += titleDepth ;
462+ }
463+ push += titleY * height ;
464+ }
465+ return push ;
466+ }
467+
468+ function needsMarginPush ( gd , title , titleHeight ) {
469+ var titleY = title . y ;
470+ var titleYanchor = title . yanchor ;
471+ var position = titleY > 0.5 ? 't' : 'b' ;
472+ var curMargin = gd . _fullLayout . margin [ position ] ;
473+ var pushMargin = 0 ;
474+ if ( title . yref === 'paper' ) {
475+ pushMargin = (
476+ titleHeight +
477+ title . pad . t +
478+ title . pad . b
479+ ) ;
480+ } else if ( title . yref === 'container' ) {
481+ pushMargin = (
482+ containerPushVal ( position , titleY , titleYanchor , gd . _fullLayout . height , titleHeight ) +
483+ title . pad . t +
484+ title . pad . b
485+ ) ;
486+ }
487+ if ( pushMargin > curMargin ) {
488+ return pushMargin ;
489+ }
490+ return 0 ;
491+ }
492+
493+ function applyTitleAutoMargin ( gd , y , pushMargin , titleHeight ) {
494+ var titleID = 'title.automargin' ;
495+ var title = gd . _fullLayout . title ;
496+ var position = title . y > 0.5 ? 't' : 'b' ;
497+ var push = {
498+ x : title . x ,
499+ y : title . y ,
500+ t : 0 ,
501+ b : 0
502+ } ;
503+ var reservedPush = { } ;
504+
505+ if ( title . yref === 'paper' && isOutsideContainer ( gd , title , position , y , titleHeight ) ) {
506+ push [ position ] = pushMargin ;
507+ } else if ( title . yref === 'container' ) {
508+ reservedPush [ position ] = pushMargin ;
509+ gd . _fullLayout . _reservedMargin [ titleID ] = reservedPush ;
510+ }
511+ Plots . allowAutoMargin ( gd , titleID ) ;
512+ Plots . autoMargin ( gd , titleID , push ) ;
513+ }
514+
418515function getMainTitleX ( fullLayout , textAnchor ) {
419516 var title = fullLayout . title ;
420517 var gs = fullLayout . _size ;
@@ -439,7 +536,6 @@ function getMainTitleY(fullLayout, dy) {
439536 var title = fullLayout . title ;
440537 var gs = fullLayout . _size ;
441538 var vPadShift = 0 ;
442-
443539 if ( dy === '0em' || ! dy ) {
444540 vPadShift = - title . pad . b ;
445541 } else if ( dy === alignmentConstants . CAP_SHIFT + 'em' ) {
@@ -459,6 +555,16 @@ function getMainTitleY(fullLayout, dy) {
459555 }
460556}
461557
558+ function getMainTitleDyAdj ( yanchor ) {
559+ if ( yanchor === 'top' ) {
560+ return alignmentConstants . CAP_SHIFT + 0.3 + 'em' ;
561+ } else if ( yanchor === 'bottom' ) {
562+ return '-0.3em' ;
563+ } else {
564+ return alignmentConstants . MID_SHIFT + 'em' ;
565+ }
566+ }
567+
462568function getMainTitleTextAnchor ( fullLayout ) {
463569 var title = fullLayout . title ;
464570
0 commit comments