Skip to content

Commit 5156e42

Browse files
committed
Merge pull request #235 from plotly/de-circular
De-circularize Titles and Axes
2 parents 21b5411 + 6856440 commit 5156e42

File tree

4 files changed

+91
-86
lines changed

4 files changed

+91
-86
lines changed

src/components/colorbar/draw.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var Fx = require('../../plots/cartesian/graph_interact');
1818
var Lib = require('../../lib');
1919
var Drawing = require('../drawing');
2020
var Color = require('../color');
21+
var Titles = require('../titles');
2122

2223
var handleAxisDefaults = require('../../plots/cartesian/axis_defaults');
2324
var handleAxisPositionDefaults = require('../../plots/cartesian/position_defaults');
@@ -253,10 +254,10 @@ module.exports = function draw(gd, id) {
253254

254255
cbAxisOut._axislayer = container.select('.cbaxis');
255256
var titleHeight = 0;
256-
if(['top','bottom'].indexOf(opts.titleside)!==-1) {
257+
if(['top', 'bottom'].indexOf(opts.titleside) !== -1) {
257258
// draw the title so we know how much room it needs
258259
// when we squish the axis
259-
Plotly.Titles.draw(gd, cbAxisOut._id + 'title');
260+
Titles.draw(gd, cbAxisOut._id + 'title');
260261
}
261262

262263
function drawAxis(){

src/components/titles/index.js

Lines changed: 83 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -9,48 +9,47 @@
99

1010
'use strict';
1111

12-
var Plotly = require('../../plotly');
1312
var d3 = require('d3');
1413
var isNumeric = require('fast-isnumeric');
1514

16-
var plots = Plotly.Plots;
15+
var Plotly = require('../../plotly');
16+
var Plots = require('../../plots/plots');
17+
var Lib = require('../../lib');
18+
var Drawing = require('../drawing');
19+
var Color = require('../color');
20+
var svgTextUtils = require('../../lib/svg_text_utils');
21+
var axisIds = require('../../plots/cartesian/axis_ids');
22+
1723

1824
var Titles = module.exports = {};
1925

20-
// titles - (re)draw titles on the axes and plot
21-
// title can be 'xtitle', 'ytitle', 'gtitle',
22-
// or empty to draw all
26+
/**
27+
* Titles - (re)draw titles on the axes and plot:
28+
* title can be 'xtitle', 'ytitle', 'gtitle'
29+
*/
2330
Titles.draw = function(gd, title) {
24-
if(!title) {
25-
Plotly.Axes.listIds(gd).forEach(function(axId) {
26-
Titles.draw(gd, axId + 'title');
27-
});
28-
Titles.draw(gd, 'gtitle');
29-
return;
30-
}
31-
3231
var fullLayout = gd._fullLayout,
3332
gs = fullLayout._size,
3433
axletter = title.charAt(0),
35-
colorbar = title.substr(1,2)==='cb';
34+
colorbar = (title.substr(1, 2) === 'cb');
3635

3736
var cbnum, cont, options;
3837

3938
if(colorbar) {
40-
var uid = title.substr(3).replace('title','');
39+
var uid = title.substr(3).replace('title', '');
4140
gd._fullData.some(function(trace, i) {
42-
if(trace.uid===uid) {
41+
if(trace.uid === uid) {
4342
cbnum = i;
4443
cont = gd.calcdata[i][0].t.cb.axis;
4544
return true;
4645
}
4746
});
4847
}
49-
else cont = fullLayout[Plotly.Axes.id2name(title.replace('title',''))] || fullLayout;
48+
else cont = fullLayout[axisIds.id2name(title.replace('title', ''))] || fullLayout;
5049

51-
var prop = cont===fullLayout ? 'title' : cont._name+'.title',
50+
var prop = (cont === fullLayout) ? 'title' : cont._name+'.title',
5251
name = colorbar ? 'colorscale' :
53-
((cont._id||axletter).toUpperCase()+' axis'),
52+
((cont._id || axletter).toUpperCase()+' axis'),
5453
font = cont.titlefont.family,
5554
fontSize = cont.titlefont.size,
5655
fontColor = cont.titlefont.color,
@@ -68,7 +67,7 @@ Titles.draw = function(gd, title) {
6867
avoidTransform;
6968

7069
// find the transform applied to the parents of the avoid selection
71-
// which doesn't get picked up by Plotly.Drawing.bBox
70+
// which doesn't get picked up by Drawing.bBox
7271
if(colorbar) {
7372
avoid.offsetLeft = gs.l;
7473
avoid.offsetTop = gs.t;
@@ -86,57 +85,61 @@ Titles.draw = function(gd, title) {
8685
if(colorbar && cont.titleside) {
8786
// argh, we only make it here if the title is on top or bottom,
8887
// not right
89-
x = gs.l+cont.titlex*gs.w;
90-
y = gs.t+(1-cont.titley)*gs.h + ((cont.titleside==='top') ?
91-
3+fontSize*0.75 : - 3-fontSize*0.25);
92-
options = {x: x, y: y, 'text-anchor':'start'};
88+
x = gs.l + cont.titlex * gs.w;
89+
y = gs.t + (1 - cont.titley) * gs.h + ((cont.titleside === 'top') ?
90+
3 + fontSize * 0.75 : - 3 - fontSize * 0.25);
91+
options = {x: x, y: y, 'text-anchor': 'start'};
9392
avoid = {};
9493

9594
// convertToTspans rotates any 'y...' by 90 degrees...
9695
// TODO: need a better solution than this hack
97-
title = 'h'+title;
96+
title = 'h' + title;
9897
}
99-
else if(axletter==='x'){
98+
else if(axletter === 'x'){
10099
xa = cont;
101-
ya = (xa.anchor==='free') ?
102-
{_offset:gs.t+(1-(xa.position||0))*gs.h, _length:0} :
103-
Plotly.Axes.getFromId(gd, xa.anchor);
104-
x = xa._offset+xa._length/2;
105-
y = ya._offset + ((xa.side==='top') ?
100+
ya = (xa.anchor === 'free') ?
101+
{_offset: gs.t + (1 - (xa.position || 0)) * gs.h, _length: 0} :
102+
axisIds.getFromId(gd, xa.anchor);
103+
104+
x = xa._offset + xa._length / 2;
105+
y = ya._offset + ((xa.side === 'top') ?
106106
-10 - fontSize*(offsetBase + (xa.showticklabels ? 1 : 0)) :
107107
ya._length + 10 +
108108
fontSize*(offsetBase + (xa.showticklabels ? 1.5 : 0.5)));
109+
109110
options = {x: x, y: y, 'text-anchor': 'middle'};
110-
if(!avoid.side) { avoid.side = 'bottom'; }
111+
if(!avoid.side) avoid.side = 'bottom';
111112
}
112-
else if(axletter==='y'){
113+
else if(axletter === 'y'){
113114
ya = cont;
114-
xa = (ya.anchor==='free') ?
115-
{_offset:gs.l+(ya.position||0)*gs.w, _length:0} :
116-
Plotly.Axes.getFromId(gd, ya.anchor);
117-
y = ya._offset+ya._length/2;
118-
x = xa._offset + ((ya.side==='right') ?
115+
xa = (ya.anchor === 'free') ?
116+
{_offset: gs.l + (ya.position || 0) * gs.w, _length: 0} :
117+
axisIds.getFromId(gd, ya.anchor);
118+
119+
y = ya._offset + ya._length / 2;
120+
x = xa._offset + ((ya.side === 'right') ?
119121
xa._length + 10 +
120122
fontSize*(offsetBase + (ya.showticklabels ? 1 : 0.5)) :
121123
-10 - fontSize*(offsetBase + (ya.showticklabels ? 0.5 : 0)));
124+
122125
options = {x: x, y: y, 'text-anchor': 'middle'};
123126
transform = {rotate: '-90', offset: 0};
124-
if(!avoid.side) { avoid.side = 'left'; }
127+
if(!avoid.side) avoid.side = 'left';
125128
}
126-
else{
129+
else {
127130
// plot title
128131
name = 'Plot';
129132
fontSize = fullLayout.titlefont.size;
130-
x = fullLayout.width/2;
131-
y = fullLayout._size.t/2;
133+
x = fullLayout.width / 2;
134+
y = fullLayout._size.t / 2;
132135
options = {x: x, y: y, 'text-anchor': 'middle'};
133136
avoid = {};
134137
}
135138

136139
var opacity = 1,
137140
isplaceholder = false,
138141
txt = cont.title.trim();
139-
if(txt === '') { opacity = 0; }
142+
if(txt === '') opacity = 0;
140143
if(txt.match(/Click to enter .+ title/)) {
141144
opacity = 0.2;
142145
isplaceholder = true;
@@ -145,20 +148,20 @@ Titles.draw = function(gd, title) {
145148
var group;
146149
if(colorbar) {
147150
group = d3.select(gd)
148-
.selectAll('.'+cont._id.substr(1)+' .cbtitle');
151+
.selectAll('.' + cont._id.substr(1) + ' .cbtitle');
149152
// this class-to-rotate thing with convertToTspans is
150153
// getting hackier and hackier... delete groups with the
151154
// wrong class
152-
var otherClass = title.charAt(0)==='h' ?
153-
title.substr(1) : ('h'+title);
154-
group.selectAll('.'+otherClass+',.'+otherClass+'-math-group')
155+
var otherClass = title.charAt(0) === 'h' ?
156+
title.substr(1) : ('h' + title);
157+
group.selectAll('.' + otherClass + ',.' + otherClass + '-math-group')
155158
.remove();
156159
}
157160
else {
158-
group = fullLayout._infolayer.selectAll('.g-'+title)
161+
group = fullLayout._infolayer.selectAll('.g-' + title)
159162
.data([0]);
160163
group.enter().append('g')
161-
.classed('g-'+title, true);
164+
.classed('g-' + title, true);
162165
}
163166

164167
var el = group.selectAll('text')
@@ -173,36 +176,36 @@ Titles.draw = function(gd, title) {
173176
.attr('class', title);
174177

175178
function titleLayout(titleEl){
176-
Plotly.Lib.syncOrAsync([drawTitle,scootTitle], titleEl);
179+
Lib.syncOrAsync([drawTitle,scootTitle], titleEl);
177180
}
178181

179182
function drawTitle(titleEl) {
180183
titleEl.attr('transform', transform ?
181184
'rotate(' + [transform.rotate, options.x, options.y] +
182-
') translate(0, '+transform.offset+')' :
185+
') translate(0, ' + transform.offset + ')' :
183186
null);
184187

185188
titleEl.style({
186189
'font-family': font,
187-
'font-size': d3.round(fontSize,2)+'px',
188-
fill: Plotly.Color.rgb(fontColor),
189-
opacity: opacity*Plotly.Color.opacity(fontColor),
190-
'font-weight': plots.fontWeight
190+
'font-size': d3.round(fontSize,2) + 'px',
191+
fill: Color.rgb(fontColor),
192+
opacity: opacity * Color.opacity(fontColor),
193+
'font-weight': Plots.fontWeight
191194
})
192195
.attr(options)
193-
.call(Plotly.util.convertToTspans)
196+
.call(svgTextUtils.convertToTspans)
194197
.attr(options);
195198

196199
titleEl.selectAll('tspan.line')
197200
.attr(options);
198-
return plots.previousPromises(gd);
201+
return Plots.previousPromises(gd);
199202
}
200203

201204
function scootTitle(titleElIn) {
202205
var titleGroup = d3.select(titleElIn.node().parentNode);
203206

204207
if(avoid && avoid.selection && avoid.side && txt){
205-
titleGroup.attr('transform',null);
208+
titleGroup.attr('transform', null);
206209

207210
// move toward avoid.side (= left, right, top, bottom) if needed
208211
// can include pad (pixels, default 2)
@@ -213,10 +216,10 @@ Titles.draw = function(gd, title) {
213216
top: 'bottom',
214217
bottom: 'top'
215218
}[avoid.side],
216-
shiftSign = (['left','top'].indexOf(avoid.side)!==-1) ?
219+
shiftSign = (['left','top'].indexOf(avoid.side) !== -1) ?
217220
-1 : 1,
218221
pad = isNumeric(avoid.pad) ? avoid.pad : 2,
219-
titlebb = Plotly.Drawing.bBox(titleGroup.node()),
222+
titlebb = Drawing.bBox(titleGroup.node()),
220223
paperbb = {
221224
left: 0,
222225
top: 0,
@@ -225,9 +228,9 @@ Titles.draw = function(gd, title) {
225228
},
226229
maxshift = colorbar ? fullLayout.width:
227230
(paperbb[avoid.side]-titlebb[avoid.side]) *
228-
((avoid.side==='left' || avoid.side==='top') ? -1 : 1);
231+
((avoid.side === 'left' || avoid.side === 'top') ? -1 : 1);
229232
// Prevent the title going off the paper
230-
if(maxshift<0) shift = maxshift;
233+
if(maxshift < 0) shift = maxshift;
231234
else {
232235
// so we don't have to offset each avoided element,
233236
// give the title the opposite offset
@@ -239,16 +242,16 @@ Titles.draw = function(gd, title) {
239242
// iterate over a set of elements (avoid.selection)
240243
// to avoid collisions with
241244
avoid.selection.each(function(){
242-
var avoidbb = Plotly.Drawing.bBox(this);
245+
var avoidbb = Drawing.bBox(this);
243246

244-
if(Plotly.Lib.bBoxIntersect(titlebb,avoidbb,pad)) {
247+
if(Lib.bBoxIntersect(titlebb, avoidbb, pad)) {
245248
shift = Math.max(shift, shiftSign * (
246249
avoidbb[avoid.side] - titlebb[backside]) + pad);
247250
}
248251
});
249252
shift = Math.min(maxshift, shift);
250253
}
251-
if(shift>0 || maxshift<0) {
254+
if(shift > 0 || maxshift < 0) {
252255
var shiftTemplate = {
253256
left: [-shift, 0],
254257
right: [shift, 0],
@@ -264,43 +267,44 @@ Titles.draw = function(gd, title) {
264267
el.attr({'data-unformatted': txt})
265268
.call(titleLayout);
266269

267-
var placeholderText = 'Click to enter '+name.replace(/\d+/,'')+' title';
270+
var placeholderText = 'Click to enter ' + name.replace(/\d+/, '') + ' title';
268271

269-
function setPlaceholder(){
272+
function setPlaceholder() {
270273
opacity = 0;
271274
isplaceholder = true;
272275
txt = placeholderText;
273-
fullLayout._infolayer.select('.'+title)
276+
fullLayout._infolayer.select('.' + title)
274277
.attr({'data-unformatted': txt})
275278
.text(txt)
276-
.on('mouseover.opacity',function(){
279+
.on('mouseover.opacity', function() {
277280
d3.select(this).transition()
278-
.duration(100).style('opacity',1);
281+
.duration(100).style('opacity', 1);
279282
})
280-
.on('mouseout.opacity',function(){
283+
.on('mouseout.opacity',function() {
281284
d3.select(this).transition()
282-
.duration(1000).style('opacity',0);
285+
.duration(1000).style('opacity', 0);
283286
});
284287
}
285288

286289
if(gd._context.editable){
287290
if(!txt) setPlaceholder();
288291

289-
el.call(Plotly.util.makeEditable)
290-
.on('edit', function(text){
292+
el.call(svgTextUtils.makeEditable)
293+
.on('edit', function(text) {
291294
if(colorbar) {
292295
var trace = gd._fullData[cbnum];
293-
if(plots.traceIs(trace, 'markerColorscale')) {
296+
if(Plots.traceIs(trace, 'markerColorscale')) {
294297
Plotly.restyle(gd, 'marker.colorbar.title', text, cbnum);
295-
} else Plotly.restyle(gd, 'colorbar.title', text, cbnum);
298+
}
299+
else Plotly.restyle(gd, 'colorbar.title', text, cbnum);
296300
}
297301
else Plotly.relayout(gd,prop,text);
298302
})
299-
.on('cancel', function(){
303+
.on('cancel', function() {
300304
this.text(this.attr('data-unformatted'))
301305
.call(titleLayout);
302306
})
303-
.on('input', function(d){
307+
.on('input', function(d) {
304308
this.text(d || ' ').attr(options)
305309
.selectAll('tspan.line')
306310
.attr(options);
@@ -309,5 +313,5 @@ Titles.draw = function(gd, title) {
309313
else if(!txt || txt.match(/Click to enter .+ title/)) {
310314
el.remove();
311315
}
312-
el.classed('js-placeholder',isplaceholder);
316+
el.classed('js-placeholder', isplaceholder);
313317
};

src/plotly.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ exports.Colorbar = require('./components/colorbar');
4848
exports.ErrorBars = require('./components/errorbars');
4949
exports.Annotations = require('./components/annotations');
5050
exports.Shapes = require('./components/shapes');
51-
exports.Titles = require('./components/titles');
5251
exports.Legend = require('./components/legend');
5352
exports.ModeBar = require('./components/modebar');
5453

@@ -59,7 +58,6 @@ exports.register = function register(_modules) {
5958
_modules = [_modules];
6059
}
6160

62-
6361
for(var i = 0; i < _modules.length; i++){
6462
var newModule = _modules[i];
6563

0 commit comments

Comments
 (0)