Skip to content

Commit

Permalink
Merge pull request #6593 from plotly/colorbar-positioning
Browse files Browse the repository at this point in the history
Add `xref` and `yref` to colorbars
  • Loading branch information
hannahker committed May 12, 2023
2 parents 44c97be + 39277d9 commit 39c2f06
Show file tree
Hide file tree
Showing 9 changed files with 1,042 additions and 254 deletions.
1 change: 1 addition & 0 deletions draftlogs/6593_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add `colorbar.xref` and `colorbar.yref` to enable container-referenced positioning for plot colorbars [[#6593](https://github.com/plotly/plotly.js/pull/6593)], with thanks to [Gamma Technologies](https://www.gtisoft.com/) for sponsoring the related development.
46 changes: 36 additions & 10 deletions src/components/colorbar/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,25 @@ module.exports = overrideAll({
},
x: {
valType: 'number',
min: -2,
max: 3,
description: [
'Sets the x position of the color bar (in plot fraction).',
'Defaults to 1.02 when `orientation` is *v* and',
'0.5 when `orientation` is *h*.'
'Sets the x position with respect to `xref` of the color bar (in plot fraction).',
'When `xref` is *paper*, defaults to 1.02 when `orientation` is *v* and',
'0.5 when `orientation` is *h*.',
'When `xref` is *container*, defaults to *1* when `orientation` is *v* and',
'0.5 when `orientation` is *h*.',
'Must be between *0* and *1* if `xref` is *container*',
'and between *-2* and *3* if `xref` is *paper*.'
].join(' ')
},
xref: {
valType: 'enumerated',
dflt: 'paper',
values: ['container', 'paper'],
editType: 'layoutstyle',
description: [
'Sets the container `x` refers to.',
'*container* spans the entire `width` of the plot.',
'*paper* refers to the width of the plotting area only.'
].join(' ')
},
xanchor: {
Expand All @@ -84,14 +97,27 @@ module.exports = overrideAll({
},
y: {
valType: 'number',
min: -2,
max: 3,
description: [
'Sets the y position of the color bar (in plot fraction).',
'Defaults to 0.5 when `orientation` is *v* and',
'1.02 when `orientation` is *h*.'
'Sets the y position with respect to `yref` of the color bar (in plot fraction).',
'When `yref` is *paper*, defaults to 0.5 when `orientation` is *v* and',
'1.02 when `orientation` is *h*.',
'When `yref` is *container*, defaults to 0.5 when `orientation` is *v* and',
'1 when `orientation` is *h*.',
'Must be between *0* and *1* if `yref` is *container*',
'and between *-2* and *3* if `yref` is *paper*.'
].join(' ')
},
yref: {
valType: 'enumerated',
dflt: 'paper',
values: ['container', 'paper'],
editType: 'layoutstyle',
description: [
'Sets the container `y` refers to.',
'*container* spans the entire `height` of the plot.',
'*paper* refers to the height of the plotting area only.'
].join(' '),
},
yanchor: {
valType: 'enumerated',
values: ['top', 'middle', 'bottom'],
Expand Down
45 changes: 41 additions & 4 deletions src/components/colorbar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,48 @@ module.exports = function colorbarDefaults(containerIn, containerOut, layout) {
isVertical ? h : w
);

coerce('x', isVertical ? 1.02 : 0.5);
coerce('xanchor', isVertical ? 'left' : 'center');
var yref = coerce('yref');
var xref = coerce('xref');

var isPaperY = yref === 'paper';
var isPaperX = xref === 'paper';

var defaultX, defaultY, defaultYAnchor;
var defaultXAnchor = 'left';

if(isVertical) {
defaultYAnchor = 'middle';
defaultXAnchor = isPaperX ? 'left' : 'right';
defaultX = isPaperX ? 1.02 : 1;
defaultY = 0.5;
} else {
defaultYAnchor = isPaperY ? 'bottom' : 'top';
defaultXAnchor = 'center';
defaultX = 0.5;
defaultY = isPaperY ? 1.02 : 1;
}

Lib.coerce(colorbarIn, colorbarOut, {
x: {
valType: 'number',
min: isPaperX ? -2 : 0,
max: isPaperX ? 3 : 1,
dflt: defaultX,
}
}, 'x');

Lib.coerce(colorbarIn, colorbarOut, {
y: {
valType: 'number',
min: isPaperY ? -2 : 0,
max: isPaperY ? 3 : 1,
dflt: defaultY,
}
}, 'y');

coerce('xanchor', defaultXAnchor);
coerce('xpad');
coerce('y', isVertical ? 0.5 : 1.02);
coerce('yanchor', isVertical ? 'middle' : 'bottom');
coerce('yanchor', defaultYAnchor);
coerce('ypad');
Lib.noneOrAll(colorbarIn, colorbarOut, ['x', 'y']);

Expand Down
73 changes: 55 additions & 18 deletions src/components/colorbar/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ function drawColorBar(g, opts, gd) {
var optsX = opts.x;
var optsY = isVertical ? opts.y : 1 - opts.y;

var isPaperY = opts.yref === 'paper';
var isPaperX = opts.xref === 'paper';

var fullLayout = gd._fullLayout;
var gs = fullLayout._size;

Expand Down Expand Up @@ -216,11 +219,14 @@ function drawColorBar(g, opts, gd) {
var lenPx = Math.round(len * (lenmode === 'fraction' ? (isVertical ? gs.h : gs.w) : 1));
var lenFrac = lenPx / (isVertical ? gs.h : gs.w);

var posW = isPaperX ? gs.w : gd._fullLayout.width;
var posH = isPaperY ? gs.h : gd._fullLayout.height;

// x positioning: do it initially just for left anchor,
// then fix at the end (since we don't know the width yet)
var uPx = Math.round(isVertical ?
optsX * gs.w + xpad :
optsY * gs.h + ypad
optsX * posW + xpad :
optsY * posH + ypad
);

var xRatio = {center: 0.5, right: 1}[xanchor] || 0;
Expand All @@ -237,8 +243,8 @@ function drawColorBar(g, opts, gd) {
optsX - xRatio * lenFrac;

var vPx = Math.round(isVertical ?
gs.h * (1 - vFrac) :
gs.w * vFrac
posH * (1 - vFrac) :
posW * vFrac
);

// stash a few things for makeEditable
Expand Down Expand Up @@ -351,18 +357,18 @@ function drawColorBar(g, opts, gd) {
var x, y;

if(titleSide === 'top') {
x = xpad + gs.l + gs.w * optsX;
y = ypad + gs.t + gs.h * (1 - vFrac - lenFrac) + 3 + titleFontSize * 0.75;
x = xpad + gs.l + posW * optsX;
y = ypad + gs.t + posH * (1 - vFrac - lenFrac) + 3 + titleFontSize * 0.75;
}

if(titleSide === 'bottom') {
x = xpad + gs.l + gs.w * optsX;
y = ypad + gs.t + gs.h * (1 - vFrac) - 3 - titleFontSize * 0.25;
x = xpad + gs.l + posW * optsX;
y = ypad + gs.t + posH * (1 - vFrac) - 3 - titleFontSize * 0.25;
}

if(titleSide === 'right') {
y = ypad + gs.t + gs.h * optsY + 3 + titleFontSize * 0.75;
x = xpad + gs.l + gs.w * vFrac;
y = ypad + gs.t + posH * optsY + 3 + titleFontSize * 0.75;
x = xpad + gs.l + posW * vFrac;
}

drawTitle(ax._id + 'title', {
Expand All @@ -382,14 +388,14 @@ function drawColorBar(g, opts, gd) {

if(titleSide === 'right') {
y = mid;
x = gs.l + gs.w * pos + 10 + titleFontSize * (
x = gs.l + posW * pos + 10 + titleFontSize * (
ax.showticklabels ? 1 : 0.5
);
} else {
x = mid;

if(titleSide === 'bottom') {
y = gs.t + gs.h * pos + 10 + (
y = gs.t + posH * pos + 10 + (
ticklabelposition.indexOf('inside') === -1 ?
ax.tickfont.size :
0
Expand All @@ -402,7 +408,7 @@ function drawColorBar(g, opts, gd) {

if(titleSide === 'top') {
var nlines = title.text.split('<br>').length;
y = gs.t + gs.h * pos + 10 - thickPx - LINE_SPACING * titleFontSize * nlines;
y = gs.t + posH * pos + 10 - thickPx - LINE_SPACING * titleFontSize * nlines;
}
}

Expand Down Expand Up @@ -668,9 +674,13 @@ function drawColorBar(g, opts, gd) {

var extraW = borderwidth + outlinewidth;

// TODO - are these the correct positions?
var lx = (isVertical ? uPx : vPx) - extraW / 2 - (isVertical ? xpad : 0);
var ly = (isVertical ? vPx : uPx) - (isVertical ? lenPx : ypad + moveY - hColorbarMoveTitle);

g.select('.' + cn.cbbg)
.attr('x', (isVertical ? uPx : vPx) - extraW / 2 - (isVertical ? xpad : 0))
.attr('y', (isVertical ? vPx : uPx) - (isVertical ? lenPx : ypad + moveY - hColorbarMoveTitle))
.attr('x', lx)
.attr('y', ly)
.attr(isVertical ? 'width' : 'height', Math.max(outerThickness - hColorbarMoveTitle, 2))
.attr(isVertical ? 'height' : 'width', Math.max(lenPx + extraW, 2))
.call(Color.fill, bgcolor)
Expand All @@ -693,9 +703,14 @@ function drawColorBar(g, opts, gd) {
'stroke-width': outlinewidth
});

var xShift = ((isVertical ? xRatio * outerThickness : 0));
var yShift = ((isVertical ? 0 : (1 - yRatio) * outerThickness - moveY));
xShift = isPaperX ? gs.l - xShift : -xShift;
yShift = isPaperY ? gs.t - yShift : -yShift;

g.attr('transform', strTranslate(
gs.l - (isVertical ? xRatio * outerThickness : 0),
gs.t - (isVertical ? 0 : (1 - yRatio) * outerThickness - moveY)
xShift,
yShift
));

if(!isVertical && (
Expand Down Expand Up @@ -802,8 +817,30 @@ function drawColorBar(g, opts, gd) {
marginOpts.yb = optsY + thickness * bFrac;
}
}
var sideY = opts.y < 0.5 ? 'b' : 't';
var sideX = opts.x < 0.5 ? 'l' : 'r';

gd._fullLayout._reservedMargin[opts._id] = {};
var possibleReservedMargins = {
r: (fullLayout.width - lx - xShift),
l: lx + marginOpts.r,
b: (fullLayout.height - ly - yShift),
t: ly + marginOpts.b
};

Plots.autoMargin(gd, opts._id, marginOpts);
if(isPaperX && isPaperY) {
Plots.autoMargin(gd, opts._id, marginOpts);
} else if(isPaperX) {
gd._fullLayout._reservedMargin[opts._id][sideY] = possibleReservedMargins[sideY];
} else if(isPaperY) {
gd._fullLayout._reservedMargin[opts._id][sideX] = possibleReservedMargins[sideX];
} else {
if(isVertical) {
gd._fullLayout._reservedMargin[opts._id][sideX] = possibleReservedMargins[sideX];
} else {
gd._fullLayout._reservedMargin[opts._id][sideY] = possibleReservedMargins[sideY];
}
}
}

return Lib.syncOrAsync([
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions test/image/mocks/zz-container-colorbar-horizontal.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"data": [
{
"colorbar": {
"orientation": "h",
"yref": "container",
"y": 0,
"yanchor": "bottom",
"thickness": 10,
"ticks": "outside",
"bgcolor": "rgba(255,255,0,0.5)",
"borderwidth": 4,
"bordercolor": "gray",
"title": {
"text": "Colorbar<br>title",
"font": {
"size": 16
}
}
},
"z": [
[
1,
3,
5
],
[
4,
7,
10
],
[
7,
11,
14
]
],
"type": "heatmap"
}
],
"layout": {
"margin": {"l": 0, "r": 0, "t": 0, "b": 0},
"height": 300,
"width": 400,
"xaxis": {"automargin": true, "title": {"text": "X-axis title"}},
"title": {"text": "Colorbar with `yref='container'` | horizontal", "automargin": true}
}
}
47 changes: 47 additions & 0 deletions test/image/mocks/zz-container-colorbar-vertical.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"data": [
{
"colorbar": {
"orientation": "v",
"xref": "container",
"thickness": 10,
"ticks": "outside",
"bgcolor": "rgba(255,255,0,0.5)",
"borderwidth": 4,
"bordercolor": "gray",
"title": {
"side": "top",
"text": "Colorbar<br>title",
"font": {
"size": 16
}
}
},
"z": [
[
1,
3,
5
],
[
4,
7,
10
],
[
7,
11,
14
]
],
"type": "heatmap"
}
],
"layout": {
"margin": {"l": 0, "r": 0, "t": 0, "b": 0},
"height": 300,
"width": 400,
"yaxis": {"automargin": true, "side": "right", "title": {"text": "Y-axis title"}},
"title": {"text": "Colorbar with `xref='container' | vertical`", "automargin": true}
}
}
Loading

0 comments on commit 39c2f06

Please sign in to comment.