Skip to content

Commit

Permalink
Merge pull request #2243 from plotly/label_automargin
Browse files Browse the repository at this point in the history
automatic margins for long labels
  • Loading branch information
nicolaskruchten authored Mar 2, 2018
2 parents 13eb982 + 1df34bd commit 12e69c8
Show file tree
Hide file tree
Showing 12 changed files with 339 additions and 42 deletions.
3 changes: 2 additions & 1 deletion src/plot_api/edit_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ var layoutOpts = {
valType: 'flaglist',
extras: ['none'],
flags: [
'calc', 'calcIfAutorange', 'plot', 'legend', 'ticks',
'calc', 'calcIfAutorange', 'plot', 'legend', 'ticks', 'margins',
'layoutstyle', 'modebar', 'camera', 'arraydraw'
],
description: [
Expand All @@ -47,6 +47,7 @@ var layoutOpts = {
'*plot* calls `Plotly.plot` but without first clearing `gd.calcdata`.',
'*legend* only redraws the legend.',
'*ticks* only redraws axis ticks, labels, and gridlines.',
'*margins* recomputes ticklabel automargins.',
'*layoutstyle* reapplies global and SVG cartesian axis styles.',
'*modebar* just updates the modebar.',
'*camera* just updates the camera settings for gl3d scenes.',
Expand Down
9 changes: 9 additions & 0 deletions src/plot_api/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,12 @@ exports.clearAxisTypes = function(gd, traces, layoutUpdate) {
}
}
};

exports.clearAxisAutomargins = function(gd) {
var keys = Object.keys(gd._fullLayout._pushmargin);
for(var i = 0; i < keys.length; i++) {
if(keys[i].indexOf('automargin') !== -1) {
delete gd._fullLayout._pushmargin[keys[i]];
}
}
};
3 changes: 3 additions & 0 deletions src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -1746,6 +1746,7 @@ exports.relayout = function relayout(gd, astr, val) {

// clear calcdata if required
if(flags.calc) gd.calcdata = undefined;
if(flags.margins) helpers.clearAxisAutomargins(gd);

// fill in redraw sequence

Expand Down Expand Up @@ -2186,6 +2187,7 @@ exports.update = function update(gd, traceUpdate, layoutUpdate, _traces) {
// clear calcdata and/or axis types if required
if(restyleFlags.clearCalc || relayoutFlags.calc) gd.calcdata = undefined;
if(restyleFlags.clearAxisTypes) helpers.clearAxisTypes(gd, traces, layoutUpdate);
if(relayoutFlags.margins) helpers.clearAxisAutomargins(gd);

// fill in redraw sequence
var seq = [];
Expand Down Expand Up @@ -2313,6 +2315,7 @@ exports.react = function(gd, data, layout, config) {

// clear calcdata if required
if(restyleFlags.calc || relayoutFlags.calc) gd.calcdata = undefined;
if(relayoutFlags.margins) helpers.clearAxisAutomargins(gd);

// Note: what restyle/relayout use impliedEdits and clearAxisTypes for
// must be handled by the user when using Plotly.react.
Expand Down
33 changes: 32 additions & 1 deletion src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

var d3 = require('d3');
var isNumeric = require('fast-isnumeric');
var Plots = require('../../plots/plots');

var Registry = require('../../registry');
var Lib = require('../../lib');
Expand Down Expand Up @@ -2217,10 +2218,40 @@ axes.doTicks = function(gd, axid, skipTitle) {
}
}

function doAutoMargins() {
if(!ax.automargin) { return; }
if(axLetter !== 'x' && axLetter !== 'y') { return; }

var s = ax.side[0];
var push = {x: 0, y: 0, r: 0, l: 0, t: 0, b: 0};

if(axLetter === 'x') {
push.y = (ax.anchor === 'free' ? ax.position :
ax._anchorAxis.domain[s === 't' ? 1 : 0]);
push[s] += ax._boundingBox.height;
}
else {
push.x = (ax.anchor === 'free' ? ax.position :
ax._anchorAxis.domain[s === 'r' ? 1 : 0]);
push[s] += ax._boundingBox.width;
}

if(ax.title !== fullLayout._dfltTitle[axLetter]) {
push[s] += ax.titlefont.size;
}

var pushKey = ax._name + '.automargin';
var prevPush = fullLayout._pushmargin[pushKey];
if(!prevPush || prevPush[s].size < push[s]) {
Plots.autoMargin(gd, pushKey, push);
}
}

var done = Lib.syncOrAsync([
allLabelsReady,
fixLabelOverlaps,
calcBoundingBox
calcBoundingBox,
doAutoMargins
]);
if(done && done.then) gd._promises.push(done);
return done;
Expand Down
2 changes: 2 additions & 0 deletions src/plots/cartesian/axis_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,5 +88,7 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce,

if(containerOut.showline || containerOut.ticks) coerce('mirror');

if(options.automargin) coerce('automargin');

return containerOut;
};
80 changes: 45 additions & 35 deletions src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ module.exports = {
title: {
valType: 'string',
role: 'info',
editType: 'ticks',
editType: 'ticks+margins',
description: 'Sets the title of this axis.'
},
titlefont: fontAttrs({
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Sets this axis\' title font.'
].join(' ')
Expand Down Expand Up @@ -100,10 +100,10 @@ module.exports = {
valType: 'info_array',
role: 'info',
items: [
{valType: 'any', editType: 'plot', impliedEdits: {'^autorange': false}},
{valType: 'any', editType: 'plot', impliedEdits: {'^autorange': false}}
{valType: 'any', editType: 'plot+margins', impliedEdits: {'^autorange': false}},
{valType: 'any', editType: 'plot+margins', impliedEdits: {'^autorange': false}}
],
editType: 'plot',
editType: 'plot+margins',
impliedEdits: {'autorange': false},
description: [
'Sets the range of this axis.',
Expand Down Expand Up @@ -198,7 +198,7 @@ module.exports = {
valType: 'enumerated',
values: ['auto', 'linear', 'array'],
role: 'info',
editType: 'ticks',
editType: 'ticks+margins',
impliedEdits: {tick0: undefined, dtick: undefined},
description: [
'Sets the tick mode for this axis.',
Expand All @@ -216,7 +216,7 @@ module.exports = {
min: 0,
dflt: 0,
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Specifies the maximum number of ticks for the particular axis.',
'The actual number of ticks will be chosen automatically to be',
Expand All @@ -227,7 +227,7 @@ module.exports = {
tick0: {
valType: 'any',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
impliedEdits: {tickmode: 'linear'},
description: [
'Sets the placement of the first tick on this axis.',
Expand All @@ -243,7 +243,7 @@ module.exports = {
dtick: {
valType: 'any',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
impliedEdits: {tickmode: 'linear'},
description: [
'Sets the step in-between ticks on this axis. Use with `tick0`.',
Expand All @@ -269,7 +269,7 @@ module.exports = {
},
tickvals: {
valType: 'data_array',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Sets the values at which ticks on this axis appear.',
'Only has an effect if `tickmode` is set to *array*.',
Expand All @@ -278,7 +278,7 @@ module.exports = {
},
ticktext: {
valType: 'data_array',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Sets the text displayed at the ticks position via `tickvals`.',
'Only has an effect if `tickmode` is set to *array*.',
Expand All @@ -289,7 +289,7 @@ module.exports = {
valType: 'enumerated',
values: ['outside', 'inside', ''],
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Determines whether ticks are drawn or not.',
'If **, this axis\' ticks are not drawn.',
Expand Down Expand Up @@ -341,9 +341,19 @@ module.exports = {
valType: 'boolean',
dflt: true,
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: 'Determines whether or not the tick labels are drawn.'
},
automargin: {
valType: 'boolean',
dflt: false,
role: 'style',
editType: 'ticks+margins',
description: [
'Determines whether long tick labels automatically grow the figure',
'margins.'
].join(' ')
},
showspikes: {
valType: 'boolean',
dflt: false,
Expand Down Expand Up @@ -396,14 +406,14 @@ module.exports = {
description: 'Determines whether spikelines are stuck to the cursor or to the closest datapoints.'
},
tickfont: fontAttrs({
editType: 'ticks',
editType: 'ticks+margins',
description: 'Sets the tick font.'
}),
tickangle: {
valType: 'angle',
dflt: 'auto',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Sets the angle of the tick labels with respect to the horizontal.',
'For example, a `tickangle` of -90 draws the tick labels',
Expand All @@ -414,15 +424,15 @@ module.exports = {
valType: 'string',
dflt: '',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: 'Sets a tick label prefix.'
},
showtickprefix: {
valType: 'enumerated',
values: ['all', 'first', 'last', 'none'],
dflt: 'all',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'If *all*, all tick labels are displayed with a prefix.',
'If *first*, only the first tick is displayed with a prefix.',
Expand All @@ -434,23 +444,23 @@ module.exports = {
valType: 'string',
dflt: '',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: 'Sets a tick label suffix.'
},
showticksuffix: {
valType: 'enumerated',
values: ['all', 'first', 'last', 'none'],
dflt: 'all',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: 'Same as `showtickprefix` but for tick suffixes.'
},
showexponent: {
valType: 'enumerated',
values: ['all', 'first', 'last', 'none'],
dflt: 'all',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'If *all*, all exponents are shown besides their significands.',
'If *first*, only the exponent of the first tick is shown.',
Expand All @@ -463,7 +473,7 @@ module.exports = {
values: ['none', 'e', 'E', 'power', 'SI', 'B'],
dflt: 'B',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Determines a formatting rule for the tick exponents.',
'For example, consider the number 1,000,000,000.',
Expand All @@ -479,7 +489,7 @@ module.exports = {
valType: 'boolean',
dflt: false,
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'If "true", even 4-digit integers are separated'
].join(' ')
Expand All @@ -488,7 +498,7 @@ module.exports = {
valType: 'string',
dflt: '',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Sets the tick label formatting rule using d3 formatting mini-languages',
'which are very similar to those in Python. For numbers, see:',
Expand All @@ -507,10 +517,10 @@ module.exports = {
valType: 'info_array',
role: 'info',
items: [
{valType: 'any', editType: 'ticks'},
{valType: 'any', editType: 'ticks'}
{valType: 'any', editType: 'ticks+margins'},
{valType: 'any', editType: 'ticks+margins'}
],
editType: 'ticks',
editType: 'ticks+margins',
description: [
'range [*min*, *max*], where *min*, *max* - dtick values',
'which describe some zoom level, it is possible to omit *min*',
Expand All @@ -521,12 +531,12 @@ module.exports = {
valType: 'string',
dflt: '',
role: 'style',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'string - dtickformat for described zoom level, the same as *tickformat*'
].join(' ')
},
editType: 'ticks'
editType: 'ticks+margins'
},
hoverformat: {
valType: 'string',
Expand Down Expand Up @@ -628,7 +638,7 @@ module.exports = {
constants.idRegex.y.toString()
],
role: 'info',
editType: 'plot',
editType: 'plot+margins',
description: [
'If set to an opposite-letter axis id (e.g. `x2`, `y`), this axis is bound to',
'the corresponding opposite-letter axis.',
Expand All @@ -641,7 +651,7 @@ module.exports = {
valType: 'enumerated',
values: ['top', 'bottom', 'left', 'right'],
role: 'info',
editType: 'plot',
editType: 'plot+margins',
description: [
'Determines whether a x (y) axis is positioned',
'at the *bottom* (*left*) or *top* (*right*)',
Expand Down Expand Up @@ -685,11 +695,11 @@ module.exports = {
valType: 'info_array',
role: 'info',
items: [
{valType: 'number', min: 0, max: 1, editType: 'plot'},
{valType: 'number', min: 0, max: 1, editType: 'plot'}
{valType: 'number', min: 0, max: 1, editType: 'plot+margins'},
{valType: 'number', min: 0, max: 1, editType: 'plot+margins'}
],
dflt: [0, 1],
editType: 'plot',
editType: 'plot+margins',
description: [
'Sets the domain of this axis (in plot fraction).'
].join(' ')
Expand All @@ -700,7 +710,7 @@ module.exports = {
max: 1,
dflt: 0,
role: 'style',
editType: 'plot',
editType: 'plot+margins',
description: [
'Sets the position of this axis in the plotting space',
'(in normalized coordinates).',
Expand Down Expand Up @@ -744,7 +754,7 @@ module.exports = {
autotick: {
valType: 'boolean',
role: 'info',
editType: 'ticks',
editType: 'ticks+margins',
description: [
'Obsolete.',
'Set `tickmode` to *auto* for old `autotick` *true* behavior.',
Expand Down
Loading

0 comments on commit 12e69c8

Please sign in to comment.