diff --git a/src/components/colorbar/draw.js b/src/components/colorbar/draw.js index 57f853006da..392d48baff9 100644 --- a/src/components/colorbar/draw.js +++ b/src/components/colorbar/draw.js @@ -172,8 +172,8 @@ module.exports = function draw(gd, id) { tickprefix: opts.tickprefix, showticksuffix: opts.showticksuffix, ticksuffix: opts.ticksuffix, - title: opts.title, - titlefont: opts.titlefont, + title: opts.title.text, + titlefont: opts.title.font, showline: true, anchor: 'free', position: 1 diff --git a/src/components/titles/index.js b/src/components/titles/index.js index ad7a6eb17b0..8cb10dafe69 100644 --- a/src/components/titles/index.js +++ b/src/components/titles/index.js @@ -63,18 +63,18 @@ Titles.draw = function(gd, titleClass, options) { var group = options.containerGroup; var fullLayout = gd._fullLayout; - var font = cont.titlefont.family; - var fontSize = cont.titlefont.size; - var fontColor = cont.titlefont.color; + var font = typeof cont.title.text !== 'undefined' ? cont.title.font.family : cont.titlefont.family; + var fontSize = typeof cont.title.text !== 'undefined' ? cont.title.font.size : cont.titlefont.size; + var fontColor = typeof cont.title.text !== 'undefined' ? cont.title.font.color : cont.titlefont.color; var opacity = 1; var isplaceholder = false; - var txt = cont.title.trim(); + var txt = typeof cont.title.text !== 'undefined' ? cont.title.text.trim() : cont.title.trim(); // only make this title editable if we positively identify its property // as one that has editing enabled. var editAttr; - if(prop === 'title') editAttr = 'titleText'; + if(prop === 'title.text') editAttr = 'titleText'; else if(prop.indexOf('axis') !== -1) editAttr = 'axisTitleText'; else if(prop.indexOf('colorbar' !== -1)) editAttr = 'colorbarTitleText'; var editable = gd._context.edits[editAttr]; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 22016271911..320a57a19ab 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -563,7 +563,7 @@ function plotPolar(gd, data, layout) { // editable title var opacity = 1; - var txt = gd._fullLayout.title; + var txt = gd._fullLayout.title.text; if(txt === '' || !txt) opacity = 0; var titleLayout = function() { @@ -597,7 +597,7 @@ function plotPolar(gd, data, layout) { var setContenteditable = function() { this.call(svgTextUtils.makeEditable, {gd: gd}) .on('edit', function(text) { - gd.framework({layout: {title: text}}); + gd.framework({layout: {title: {text: text}}}); this.text(text) .call(titleLayout); this.call(setContenteditable); diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 78fae75a679..6981f6c743e 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -22,6 +22,8 @@ var ModeBar = require('../components/modebar'); var initInteractions = require('../plots/cartesian/graph_interact'); var cartesianConstants = require('../plots/cartesian/constants'); +var anchorUtils = require('../components/legend/anchor_utils'); + exports.layoutStyles = function(gd) { return Lib.syncOrAsync([Plots.doAutoMargin, exports.lsInner], gd); }; @@ -410,16 +412,40 @@ function findCounterAxisLineWidth(gd, ax, subplotCounterLineWidth, exports.drawMainTitle = function(gd) { var fullLayout = gd._fullLayout; + var title = fullLayout.title; + var gs = fullLayout._size; + + var attributes = { + x: title.xref === 'paper' ? gs.l + gs.w * title.x : (gs.l + gs.w + gs.r) * title.x, + y: title.yref === 'paper' ? gs.t + gs.h * (1 - title.y) : (gs.t + gs.h + gs.b) * (1 - title.y), + 'text-anchor': 'start' + }; + + if(anchorUtils.isRightAnchor(title)) { + attributes['text-anchor'] = 'end'; + attributes.x -= title.xpad; + } else if(anchorUtils.isCenterAnchor(title)) { + attributes['text-anchor'] = 'middle'; + } else { + attributes.x += title.xpad; + } + + if(anchorUtils.isBottomAnchor(title)) { + attributes.y += title.yref === 'paper' ? title.font.size : 0; + attributes.y -= title.ypad; + } else if(anchorUtils.isMiddleAnchor(title)) { + attributes.y -= title.font.size / 2; + } else if(!anchorUtils.isBottomAnchor(title) && !anchorUtils.isMiddleAnchor(title)) { + attributes.y += title.yref === 'container' ? title.font.size : 0; + attributes.y += title.ypad; + } Titles.draw(gd, 'gtitle', { propContainer: fullLayout, - propName: 'title', + propName: 'title.text', + dfltName: 'Plot', placeholder: fullLayout._dfltTitle.plot, - attributes: { - x: fullLayout.width / 2, - y: fullLayout._size.t / 2, - 'text-anchor': 'middle' - } + attributes: attributes }); }; diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 86f473a6afb..240c1314e3b 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -26,17 +26,99 @@ globalFont.color.dflt = colorAttrs.defaultLine; module.exports = { font: globalFont, title: { - valType: 'string', - role: 'info', - editType: 'layoutstyle', - description: [ - 'Sets the plot\'s title.' - ].join(' ') + text: { + valType: 'string', + role: 'info', + dflt: 'Click to enter Plot title', + editType: 'layoutstyle', + description: [ + 'Sets the plot\'s title.' + ].join(' ') + }, + x: { + valType: 'number', + role: 'info', + min: -2, + max: 3, + editType: 'none', + description: [ + 'Sets the plot\'s title x position (in px).' + ].join(' ') + }, + y: { + valType: 'number', + role: 'info', + min: -2, + max: 3, + editType: 'none', + description: [ + 'Sets the plot\'s title y position (in px).' + ].join(' ') + }, + xanchor: { + valType: 'string', + role: 'info', + dflt: 'center', + editType: 'none', + description: [ + 'Sets the plot\'s title x anchor.' + ].join(' ') + }, + yanchor: { + valType: 'string', + role: 'info', + dflt: 'auto', + editType: 'none', + description: [ + 'Sets the plot\'s title y anchor.' + ].join(' ') + }, + xref: { + valType: 'string', + role: 'info', + dflt: 'paper', + editType: 'none', + description: [ + 'Sets the plot\'s title x reference.' + ].join(' ') + }, + yref: { + valType: 'string', + role: 'info', + dflt: 'paper', + editType: 'none', + description: [ + 'Sets the plot\'s title y reference.' + ].join(' ') + }, + font: fontAttrs({ + editType: 'layoutstyle', + description: 'Sets the title font.' + }), + xpad: { + valType: 'number', + role: 'info', + min: 0, + dflt: 0, + editType: 'calc', + description: [ + 'Sets the amount of x padding (in px)', + 'between the plotting area and the title' + ].join(' ') + }, + ypad: { + valType: 'number', + role: 'info', + min: 0, + dflt: 0, + editType: 'calc', + description: [ + 'Sets the amount of y padding (in px)', + 'between the plotting area and the title' + ].join(' ') + }, + editType: 'none' }, - titlefont: fontAttrs({ - editType: 'layoutstyle', - description: 'Sets the title font.' - }), autosize: { valType: 'boolean', role: 'info', diff --git a/src/plots/plots.js b/src/plots/plots.js index db5d224674a..fd55eab7599 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1151,9 +1151,17 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) { var globalFont = Lib.coerceFont(coerce, 'font'); - coerce('title', layoutOut._dfltTitle.plot); - - Lib.coerceFont(coerce, 'titlefont', { + coerce('title.text', layoutOut._dfltTitle.plot); + coerce('title.xanchor'); + coerce('title.yanchor'); + coerce('title.xref'); + coerce('title.yref'); + coerce('title.x'); + coerce('title.y'); + coerce('title.xpad'); + coerce('title.ypad'); + + Lib.coerceFont(coerce, 'title.font', { family: globalFont.family, size: Math.round(globalFont.size * 1.4), color: globalFont.color diff --git a/src/snapshot/cloneplot.js b/src/snapshot/cloneplot.js index a03e2667b6c..14c8ec762a5 100644 --- a/src/snapshot/cloneplot.js +++ b/src/snapshot/cloneplot.js @@ -25,7 +25,9 @@ function cloneLayoutOverride(tileClass) { autosize: true, width: 150, height: 150, - title: '', + title: { + text: '' + }, showlegend: false, margin: {l: 5, r: 5, t: 5, b: 5, pad: 0}, annotations: [] @@ -34,7 +36,9 @@ function cloneLayoutOverride(tileClass) { case 'thumbnail': override = { - title: '', + title: { + text: '' + }, hidesources: true, showlegend: false, borderwidth: 0, diff --git a/test/image/mocks/14.json b/test/image/mocks/14.json index 84e03cd181c..8dc65ba2fc9 100644 --- a/test/image/mocks/14.json +++ b/test/image/mocks/14.json @@ -97,12 +97,15 @@ } ], "layout": { - "title": "Silicon Photovoltaics Learning Curve", - "titlefont": { - "color": "", - "family": "", - "size": 0 - }, + "title": { + "text": "test", + "alignment": "left", + "font": { + "color": "", + "family": "", + "size": 0 + } + }, "font": { "family": "Arial, sans-serif", "size": 12, diff --git a/test/jasmine/tests/snapshot_test.js b/test/jasmine/tests/snapshot_test.js index b48a0f2858e..510180378be 100644 --- a/test/jasmine/tests/snapshot_test.js +++ b/test/jasmine/tests/snapshot_test.js @@ -35,7 +35,9 @@ describe('Plotly.Snapshot', function() { data = [dummyTrace1, dummyTrace2]; layout = { - title: 'Chart Title', + title: { + text: 'Chart Title' + }, showlegend: true, autosize: true, width: 688, @@ -69,7 +71,9 @@ describe('Plotly.Snapshot', function() { autosize: true, width: 150, height: 150, - title: '', + title: { + text: '' + }, showlegend: false, margin: {'l': 5, 'r': 5, 't': 5, 'b': 5, 'pad': 0}, annotations: [] diff --git a/test/jasmine/tests/titles_test.js b/test/jasmine/tests/titles_test.js index 0e81784bb29..c9220038c0c 100644 --- a/test/jasmine/tests/titles_test.js +++ b/test/jasmine/tests/titles_test.js @@ -83,7 +83,9 @@ describe('editable titles', function() { Plotly.plot(gd, data, { xaxis: {title: ''}, yaxis: {title: ''}, - title: '' + title: { + text: '' + } }, {editable: true}) .then(function() { return Promise.all([ @@ -99,7 +101,9 @@ describe('editable titles', function() { Plotly.plot(gd, data, { xaxis: {title: ''}, yaxis: {title: ''}, - title: '' + title: { + text: '' + } }, {editable: true}) .then(function() { return editTitle('x', 'xaxis.title', 'XXX'); @@ -108,7 +112,7 @@ describe('editable titles', function() { return editTitle('y', 'yaxis.title', 'YYY'); }) .then(function() { - return editTitle('g', 'title', 'TTT'); + return editTitle('g', 'title.text', 'TTT'); }) .then(function() { return Promise.all([ diff --git a/test/jasmine/tests/validate_test.js b/test/jasmine/tests/validate_test.js index d5597f23322..b1894be6131 100644 --- a/test/jasmine/tests/validate_test.js +++ b/test/jasmine/tests/validate_test.js @@ -18,7 +18,9 @@ describe('Plotly.validate', function() { type: 'scatter', x: [1, 2, 3] }], { - title: 'my simple graph' + title: { + text: 'my simple graph' + } }); expect(out).toBeUndefined(); @@ -362,7 +364,9 @@ describe('Plotly.validate', function() { }] }), ], { - title: 'my transformed graph' + title: { + text: 'my transformed graph' + } }); expect(out.length).toEqual(5);