From 3ac4f0ff92a04c50a52a7f6ce8589e228ca28ec2 Mon Sep 17 00:00:00 2001 From: Alex Vinober Date: Tue, 5 Dec 2017 13:53:01 -0500 Subject: [PATCH 1/2] Chart Title Alignment --- src/components/colorbar/draw.js | 4 +- src/components/titles/index.js | 10 ++--- src/plot_api/plot_api.js | 4 +- src/plot_api/subroutines.js | 60 ++++++++++++++++++++++++++--- src/plots/layout_attributes.js | 53 ++++++++++++++++++++----- src/plots/plots.js | 7 +++- src/snapshot/cloneplot.js | 8 +++- test/image/mocks/14.json | 15 +++++--- test/jasmine/tests/snapshot_test.js | 8 +++- test/jasmine/tests/titles_test.js | 10 +++-- test/jasmine/tests/validate_test.js | 8 +++- 11 files changed, 145 insertions(+), 42 deletions(-) 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..ebb9f268206 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -410,16 +410,64 @@ function findCounterAxisLineWidth(gd, ax, subplotCounterLineWidth, exports.drawMainTitle = function(gd) { var fullLayout = gd._fullLayout; + var titleAlignment = fullLayout.title.alignment; + var attributes; + + switch(titleAlignment) { + case 'top': + attributes = { + x: fullLayout.width / 2, + y: fullLayout.title.font.size / 2, + 'text-anchor': 'middle', + 'dominant-baseline': 'hanging' + }; + break; + case 'bottom': + attributes = { + x: (fullLayout.width / 2), + y: fullLayout._size.t - (fullLayout.title.font.size / 2), + 'text-anchor': 'middle', + 'dominant-baseline': 'auto' + }; + break; + case 'left': + attributes = { + x: fullLayout.title.font.size / 2, + y: fullLayout._size.t / 2, + 'text-anchor': 'start', + 'dominant-baseline': 'middle' + }; + break; + case 'right': + attributes = { + x: fullLayout.width - (fullLayout.title.font.size / 2), + y: fullLayout._size.t / 2, + 'text-anchor': 'end', + 'dominant-baseline': 'middle' + }; + break; + case 'medium': + attributes = { + x: fullLayout.width / 2, + y: fullLayout._size.t / 2, + 'text-anchor': 'middle' + }; + break; + default: + attributes = { + x: fullLayout.title.x, + y: fullLayout.title.y, + 'text-anchor': 'start', + 'dominant-baseline': 'hanging' + }; + } 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..4152b018125 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -26,17 +26,50 @@ 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(' ') + }, + alignment: { + valType: 'string', + role: 'info', + dflt: 'middle', + editType: 'none', + description: [ + 'Sets the plot\'s title alignment' + ].join(' ') + }, + x: { + valType: 'number', + role: 'info', + min: 0, + dflt: 0, + editType: 'none', + description: [ + 'Sets the plot\'s title x position (in px).' + ].join(' ') + }, + y: { + valType: 'number', + role: 'info', + min: 0, + dflt: 0, + editType: 'none', + description: [ + 'Sets the plot\'s title y position (in px).' + ].join(' ') + }, + font: fontAttrs({ + editType: 'layoutstyle', + description: 'Sets the title font.' + }), + 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..63b0c468b60 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1151,9 +1151,12 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) { var globalFont = Lib.coerceFont(coerce, 'font'); - coerce('title', layoutOut._dfltTitle.plot); + coerce('title.text', layoutOut._dfltTitle.plot); + coerce('title.alignment'); + coerce('title.x'); + coerce('title.y'); - Lib.coerceFont(coerce, 'titlefont', { + 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); From 2c1b2269fbad1e992d3ce05ac4c64228c8e11e87 Mon Sep 17 00:00:00 2001 From: Alex Vinober Date: Fri, 29 Dec 2017 15:39:00 -0500 Subject: [PATCH 2/2] Title alignment update - attributes set changed --- src/plot_api/subroutines.js | 78 ++++++++++++---------------------- src/plots/layout_attributes.js | 75 ++++++++++++++++++++++++++------ src/plots/plots.js | 7 ++- 3 files changed, 96 insertions(+), 64 deletions(-) diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index ebb9f268206..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,56 +412,32 @@ function findCounterAxisLineWidth(gd, ax, subplotCounterLineWidth, exports.drawMainTitle = function(gd) { var fullLayout = gd._fullLayout; - var titleAlignment = fullLayout.title.alignment; - var attributes; - - switch(titleAlignment) { - case 'top': - attributes = { - x: fullLayout.width / 2, - y: fullLayout.title.font.size / 2, - 'text-anchor': 'middle', - 'dominant-baseline': 'hanging' - }; - break; - case 'bottom': - attributes = { - x: (fullLayout.width / 2), - y: fullLayout._size.t - (fullLayout.title.font.size / 2), - 'text-anchor': 'middle', - 'dominant-baseline': 'auto' - }; - break; - case 'left': - attributes = { - x: fullLayout.title.font.size / 2, - y: fullLayout._size.t / 2, - 'text-anchor': 'start', - 'dominant-baseline': 'middle' - }; - break; - case 'right': - attributes = { - x: fullLayout.width - (fullLayout.title.font.size / 2), - y: fullLayout._size.t / 2, - 'text-anchor': 'end', - 'dominant-baseline': 'middle' - }; - break; - case 'medium': - attributes = { - x: fullLayout.width / 2, - y: fullLayout._size.t / 2, - 'text-anchor': 'middle' - }; - break; - default: - attributes = { - x: fullLayout.title.x, - y: fullLayout.title.y, - 'text-anchor': 'start', - 'dominant-baseline': 'hanging' - }; + 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', { diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 4152b018125..240c1314e3b 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -35,20 +35,11 @@ module.exports = { 'Sets the plot\'s title.' ].join(' ') }, - alignment: { - valType: 'string', - role: 'info', - dflt: 'middle', - editType: 'none', - description: [ - 'Sets the plot\'s title alignment' - ].join(' ') - }, x: { valType: 'number', role: 'info', - min: 0, - dflt: 0, + min: -2, + max: 3, editType: 'none', description: [ 'Sets the plot\'s title x position (in px).' @@ -57,17 +48,75 @@ module.exports = { y: { valType: 'number', role: 'info', - min: 0, - dflt: 0, + 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' }, autosize: { diff --git a/src/plots/plots.js b/src/plots/plots.js index 63b0c468b60..fd55eab7599 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1152,9 +1152,14 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) { var globalFont = Lib.coerceFont(coerce, 'font'); coerce('title.text', layoutOut._dfltTitle.plot); - coerce('title.alignment'); + 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,