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
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