Skip to content

Add xref and yref to colorbars #6593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 12, 2023
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
@@ -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: {
@@ -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'],
45 changes: 41 additions & 4 deletions src/components/colorbar/defaults.js
Original file line number Diff line number Diff line change
@@ -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']);

73 changes: 55 additions & 18 deletions src/components/colorbar/draw.js
Original file line number Diff line number Diff line change
@@ -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;

@@ -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;
@@ -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
@@ -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', {
@@ -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
@@ -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;
}
}

@@ -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)
@@ -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 && (
@@ -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([
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}
}
}
1,036 changes: 814 additions & 222 deletions test/plot-schema.json

Large diffs are not rendered by default.