Skip to content
4 changes: 4 additions & 0 deletions src/components/annotations/annotation_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return annOut;

coerce('opacity');
coerce('align');
coerce('bgcolor');
Expand Down
9 changes: 9 additions & 0 deletions src/components/annotations/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this annotation is visible.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ var draw = require('./draw').draw;

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = fullLayout.annotations;
annotationList = Lib.filterVisible(fullLayout.annotations);

if(!annotationList.length || !gd._fullData.length) return;

Expand Down
10 changes: 7 additions & 3 deletions src/components/annotations/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,9 @@ function draw(gd) {
fullLayout._infolayer.selectAll('.annotation').remove();

for(var i = 0; i < fullLayout.annotations.length; i++) {
drawOne(gd, i);
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}

return Plots.previousPromises(gd);
Expand DownExpand Up@@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) {
// where we fail here when they add/remove annotations
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input annotation as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

// return early in visible: false updates
if(optionsIn.visible === false) return;

var gs = fullLayout._size;
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

var axLetters = ['x', 'y'];
for(i = 0; i < 2; i++) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/images/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this image is visible.'
].join(' ')
},

source: {
valType: 'string',
role: 'info',
Expand Down
36 changes: 17 additions & 19 deletions src/components/images/defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var attributes = require('./attributes');

var name = 'images';

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [],
contOut = layoutOut[name] = [];

if(!layoutIn.images || !Array.isArray(layoutIn.images)) return;
for(var i = 0; i < contIn.length; i++) {
var itemIn = contIn[i] || {},
itemOut = {};

imageDefaults(itemIn, itemOut, layoutOut);

var containerIn = layoutIn.images,
containerOut = layoutOut.images = [];


for(var i = 0; i < containerIn.length; i++) {
var image = containerIn[i];

if(!image.source) continue;

var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
containerOut.push(defaulted);
contOut.push(itemOut);
}
};


function imageDefaults(imageIn, imageOut, fullLayout) {

imageOut = imageOut || {};

function coerce(attr, dflt) {
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
}

coerce('source');
var source = coerce('source');
var visible = coerce('visible', !!source);

if(!visible) return imageOut;

coerce('layer');
coerce('x');
coerce('y');
Expand All@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
coerce('sizing');
coerce('opacity');

for(var i = 0; i < 2; i++) {
var tdMock = { _fullLayout: fullLayout },
axLetter = ['x', 'y'][i];
var gdMock = { _fullLayout: fullLayout },
axLetters = ['x', 'y'];

for(var i = 0; i < 2; i++) {
// 'paper' is the fallback axref
Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper');
Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper');
}

return imageOut;
Expand Down
18 changes: 8 additions & 10 deletions src/components/images/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

module.exports = function draw(gd) {

var fullLayout = gd._fullLayout,
imageDataAbove = [],
imageDataSubplot = [],
imageDataBelow = [];

if(!fullLayout.images) return;


// Sort into top, subplot, and bottom layers
for(var i = 0; i < fullLayout.images.length; i++) {
var img = fullLayout.images[i];

if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
if(img.visible) {
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/shapes/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this shape is visible.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'rect', 'path', 'line'],
Expand Down
3 changes: 2 additions & 1 deletion src/components/shapes/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
Expand All@@ -17,7 +18,7 @@ var helpers = require('./helpers');

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
shapeList = fullLayout.shapes;
shapeList = Lib.filterVisible(fullLayout.shapes);

if(!shapeList.length || !gd._fullData.length) return;

Expand Down
13 changes: 9 additions & 4 deletions src/components/shapes/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,9 @@ function draw(gd) {
fullLayout._shapeSubplotLayer.selectAll('path').remove();

for(var i = 0; i < fullLayout.shapes.length; i++) {
drawOne(gd, i);
if(fullLayout.shapes[i].visible) {
drawOne(gd, i);
}
}

// may need to resurrect this if we put text (LaTeX) in shapes
Expand DownExpand Up@@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) {
// TODO: clean this up and remove it.
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input shape as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

var posAttrs = ['x0', 'x1', 'y0', 'y1'];
// return early in visible: false updates
if(optionsIn.visible === false) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
posAttrs = ['x0', 'x1', 'y0', 'y1'];

for(i = 0; i < 4; i++) {
var posAttr = posAttrs[i];
// if we don't have an explicit position already,
Expand Down
4 changes: 4 additions & 0 deletions src/components/shapes/shape_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return shapeOut;

coerce('layer');
coerce('opacity');
coerce('fillcolor');
Expand Down
19 changes: 13 additions & 6 deletions src/lib/filter_visible.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,14 +9,21 @@

'use strict';

module.exports = function filterVisible(dataIn) {
var dataOut = [];
/** Filter out object items with visible !== true
* insider array container.
*
* @param {array of objects} container
* @return {array of objects} of length <= container
*
*/
module.exports = function filterVisible(container) {
var out = [];

for(var i = 0; i < dataIn.length; i++) {
var trace = dataIn[i];
for(var i = 0; i < container.length; i++) {
var item = container[i];

if(trace.visible === true) dataOut.push(trace);
if(item.visible === true) out.push(item);
}

return dataOut;
return out;
};
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
lib.notifier = require('./notifier');

lib.filterUnique = require('./filter_unique');
lib.filterVisible = require('./filter_visible');


/**
* swap x and y of the same attribute in container cont
Expand Down
7 changes: 4 additions & 3 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
objList = layout[objType] || [],
obji = objList[objNum] || {};

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

// if p.parts is just an annotation number, and val is either
// 'add' or an entire annotation to add, the undo is 'remove'
// if val is 'remove' then undo is the whole annotation object
if(p.parts.length === 2) {

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
undoit[ai] = 'remove';
}
Expand Down
3 changes: 1 addition & 2 deletions src/plots/ternary/ternary.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
var setConvert = require('../cartesian/set_convert');
var extendFlat = require('../../lib/extend').extendFlat;
var Axes = require('../cartesian/axes');
var filterVisible = require('../../lib/filter_visible');
var dragElement = require('../../components/dragelement');
var Titles = require('../../components/titles');
var prepSelect = require('../cartesian/select');
Expand DownExpand Up@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
var moduleData = traceHash[moduleNames[i]];
var _module = moduleData[0]._module;

_module.plot(_this, filterVisible(moduleData), ternaryLayout);
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
}

_this.traceHash = traceHash;
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/annotations.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
{"text":"opacity","opacity":0.5,"x":5,"y":5},
{"text":"not-visible", "visible": false},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock is not autoranged... do you want to also include an invisible annotation in annotations-autorange.json, positioned to verify that it doesn't contribute? Same for shapes.

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to also include an invisible annotation in annotations-autorange.json

done in a2dd634

Same for shapes.

the shape_below_traces is autoranged, so that's a ✅ already

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

I'd say that's a 🐛

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shape_below_traces is autoranged, so that's a ✅ already

but the shape you added there just says {visible: false} so it will get auto coordinates too, which are going to be within the range chosen by autorange anyway, ie including it in autorange wouldn't make any difference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(actually that's true of a2dd634 too right?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson good 👁️

I chose to revert a2dd634 and test annotations and shapes autorange using jasmine tests in 5e62c08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the new tests look great. Definitely better to do this with jasmine and relayout.

{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/layout_image.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,19 @@
"opacity": 0.4,
"layer": "below"
},
{
"visible": false,
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
"xref": "x",
"yref": "y",
"x": 1,
"y": 3,
"sizex": 2,
"sizey": 2,
"sizing": "stretch",
"opacity": 0.4,
"layer": "below"
},
{
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
"xref": "x",
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/shapes_below_traces.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,6 +91,7 @@
"y1": 1,
"yref": "paper"
},
{ "visible": false },
{
"fillcolor": "#f6e8c3",
"layer": "below",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add `visible` attribute to layout container items. by etpinard · Pull Request #1110 · plotly/plotly.js · GitHub
Skip to content
4 changes: 4 additions & 0 deletions src/components/annotations/annotation_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return annOut;

coerce('opacity');
coerce('align');
coerce('bgcolor');
Expand Down
9 changes: 9 additions & 0 deletions src/components/annotations/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this annotation is visible.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ var draw = require('./draw').draw;

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = fullLayout.annotations;
annotationList = Lib.filterVisible(fullLayout.annotations);

if(!annotationList.length || !gd._fullData.length) return;

Expand Down
10 changes: 7 additions & 3 deletions src/components/annotations/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,9 @@ function draw(gd) {
fullLayout._infolayer.selectAll('.annotation').remove();

for(var i = 0; i < fullLayout.annotations.length; i++) {
drawOne(gd, i);
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}

return Plots.previousPromises(gd);
Expand DownExpand Up@@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) {
// where we fail here when they add/remove annotations
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input annotation as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

// return early in visible: false updates
if(optionsIn.visible === false) return;

var gs = fullLayout._size;
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

var axLetters = ['x', 'y'];
for(i = 0; i < 2; i++) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/images/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this image is visible.'
].join(' ')
},

source: {
valType: 'string',
role: 'info',
Expand Down
36 changes: 17 additions & 19 deletions src/components/images/defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var attributes = require('./attributes');

var name = 'images';

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [],
contOut = layoutOut[name] = [];

if(!layoutIn.images || !Array.isArray(layoutIn.images)) return;
for(var i = 0; i < contIn.length; i++) {
var itemIn = contIn[i] || {},
itemOut = {};

imageDefaults(itemIn, itemOut, layoutOut);

var containerIn = layoutIn.images,
containerOut = layoutOut.images = [];


for(var i = 0; i < containerIn.length; i++) {
var image = containerIn[i];

if(!image.source) continue;

var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
containerOut.push(defaulted);
contOut.push(itemOut);
}
};


function imageDefaults(imageIn, imageOut, fullLayout) {

imageOut = imageOut || {};

function coerce(attr, dflt) {
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
}

coerce('source');
var source = coerce('source');
var visible = coerce('visible', !!source);

if(!visible) return imageOut;

coerce('layer');
coerce('x');
coerce('y');
Expand All@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
coerce('sizing');
coerce('opacity');

for(var i = 0; i < 2; i++) {
var tdMock = { _fullLayout: fullLayout },
axLetter = ['x', 'y'][i];
var gdMock = { _fullLayout: fullLayout },
axLetters = ['x', 'y'];

for(var i = 0; i < 2; i++) {
// 'paper' is the fallback axref
Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper');
Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper');
}

return imageOut;
Expand Down
18 changes: 8 additions & 10 deletions src/components/images/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

module.exports = function draw(gd) {

var fullLayout = gd._fullLayout,
imageDataAbove = [],
imageDataSubplot = [],
imageDataBelow = [];

if(!fullLayout.images) return;


// Sort into top, subplot, and bottom layers
for(var i = 0; i < fullLayout.images.length; i++) {
var img = fullLayout.images[i];

if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
if(img.visible) {
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/shapes/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this shape is visible.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'rect', 'path', 'line'],
Expand Down
3 changes: 2 additions & 1 deletion src/components/shapes/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
Expand All@@ -17,7 +18,7 @@ var helpers = require('./helpers');

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
shapeList = fullLayout.shapes;
shapeList = Lib.filterVisible(fullLayout.shapes);

if(!shapeList.length || !gd._fullData.length) return;

Expand Down
13 changes: 9 additions & 4 deletions src/components/shapes/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,9 @@ function draw(gd) {
fullLayout._shapeSubplotLayer.selectAll('path').remove();

for(var i = 0; i < fullLayout.shapes.length; i++) {
drawOne(gd, i);
if(fullLayout.shapes[i].visible) {
drawOne(gd, i);
}
}

// may need to resurrect this if we put text (LaTeX) in shapes
Expand DownExpand Up@@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) {
// TODO: clean this up and remove it.
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input shape as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

var posAttrs = ['x0', 'x1', 'y0', 'y1'];
// return early in visible: false updates
if(optionsIn.visible === false) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
posAttrs = ['x0', 'x1', 'y0', 'y1'];

for(i = 0; i < 4; i++) {
var posAttr = posAttrs[i];
// if we don't have an explicit position already,
Expand Down
4 changes: 4 additions & 0 deletions src/components/shapes/shape_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return shapeOut;

coerce('layer');
coerce('opacity');
coerce('fillcolor');
Expand Down
19 changes: 13 additions & 6 deletions src/lib/filter_visible.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,14 +9,21 @@

'use strict';

module.exports = function filterVisible(dataIn) {
var dataOut = [];
/** Filter out object items with visible !== true
* insider array container.
*
* @param {array of objects} container
* @return {array of objects} of length <= container
*
*/
module.exports = function filterVisible(container) {
var out = [];

for(var i = 0; i < dataIn.length; i++) {
var trace = dataIn[i];
for(var i = 0; i < container.length; i++) {
var item = container[i];

if(trace.visible === true) dataOut.push(trace);
if(item.visible === true) out.push(item);
}

return dataOut;
return out;
};
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
lib.notifier = require('./notifier');

lib.filterUnique = require('./filter_unique');
lib.filterVisible = require('./filter_visible');


/**
* swap x and y of the same attribute in container cont
Expand Down
7 changes: 4 additions & 3 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
objList = layout[objType] || [],
obji = objList[objNum] || {};

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

// if p.parts is just an annotation number, and val is either
// 'add' or an entire annotation to add, the undo is 'remove'
// if val is 'remove' then undo is the whole annotation object
if(p.parts.length === 2) {

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
undoit[ai] = 'remove';
}
Expand Down
3 changes: 1 addition & 2 deletions src/plots/ternary/ternary.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
var setConvert = require('../cartesian/set_convert');
var extendFlat = require('../../lib/extend').extendFlat;
var Axes = require('../cartesian/axes');
var filterVisible = require('../../lib/filter_visible');
var dragElement = require('../../components/dragelement');
var Titles = require('../../components/titles');
var prepSelect = require('../cartesian/select');
Expand DownExpand Up@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
var moduleData = traceHash[moduleNames[i]];
var _module = moduleData[0]._module;

_module.plot(_this, filterVisible(moduleData), ternaryLayout);
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
}

_this.traceHash = traceHash;
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/annotations.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
{"text":"opacity","opacity":0.5,"x":5,"y":5},
{"text":"not-visible", "visible": false},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock is not autoranged... do you want to also include an invisible annotation in annotations-autorange.json, positioned to verify that it doesn't contribute? Same for shapes.

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to also include an invisible annotation in annotations-autorange.json

done in a2dd634

Same for shapes.

the shape_below_traces is autoranged, so that's a ✅ already

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

I'd say that's a 🐛

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shape_below_traces is autoranged, so that's a ✅ already

but the shape you added there just says {visible: false} so it will get auto coordinates too, which are going to be within the range chosen by autorange anyway, ie including it in autorange wouldn't make any difference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(actually that's true of a2dd634 too right?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson good 👁️

I chose to revert a2dd634 and test annotations and shapes autorange using jasmine tests in 5e62c08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the new tests look great. Definitely better to do this with jasmine and relayout.

{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/layout_image.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,19 @@
"opacity": 0.4,
"layer": "below"
},
{
"visible": false,
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
"xref": "x",
"yref": "y",
"x": 1,
"y": 3,
"sizex": 2,
"sizey": 2,
"sizing": "stretch",
"opacity": 0.4,
"layer": "below"
},
{
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
"xref": "x",
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/shapes_below_traces.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,6 +91,7 @@
"y1": 1,
"yref": "paper"
},
{ "visible": false },
{
"fillcolor": "#f6e8c3",
"layer": "below",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `visible` attribute to layout container items. by etpinard · Pull Request #1110 · plotly/plotly.js · GitHub
Skip to content
4 changes: 4 additions & 0 deletions src/components/annotations/annotation_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return annOut;

coerce('opacity');
coerce('align');
coerce('bgcolor');
Expand Down
9 changes: 9 additions & 0 deletions src/components/annotations/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this annotation is visible.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ var draw = require('./draw').draw;

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = fullLayout.annotations;
annotationList = Lib.filterVisible(fullLayout.annotations);

if(!annotationList.length || !gd._fullData.length) return;

Expand Down
10 changes: 7 additions & 3 deletions src/components/annotations/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,9 @@ function draw(gd) {
fullLayout._infolayer.selectAll('.annotation').remove();

for(var i = 0; i < fullLayout.annotations.length; i++) {
drawOne(gd, i);
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}

return Plots.previousPromises(gd);
Expand DownExpand Up@@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) {
// where we fail here when they add/remove annotations
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input annotation as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

// return early in visible: false updates
if(optionsIn.visible === false) return;

var gs = fullLayout._size;
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

var axLetters = ['x', 'y'];
for(i = 0; i < 2; i++) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/images/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this image is visible.'
].join(' ')
},

source: {
valType: 'string',
role: 'info',
Expand Down
36 changes: 17 additions & 19 deletions src/components/images/defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var attributes = require('./attributes');

var name = 'images';

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [],
contOut = layoutOut[name] = [];

if(!layoutIn.images || !Array.isArray(layoutIn.images)) return;
for(var i = 0; i < contIn.length; i++) {
var itemIn = contIn[i] || {},
itemOut = {};

imageDefaults(itemIn, itemOut, layoutOut);

var containerIn = layoutIn.images,
containerOut = layoutOut.images = [];


for(var i = 0; i < containerIn.length; i++) {
var image = containerIn[i];

if(!image.source) continue;

var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
containerOut.push(defaulted);
contOut.push(itemOut);
}
};


function imageDefaults(imageIn, imageOut, fullLayout) {

imageOut = imageOut || {};

function coerce(attr, dflt) {
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
}

coerce('source');
var source = coerce('source');
var visible = coerce('visible', !!source);

if(!visible) return imageOut;

coerce('layer');
coerce('x');
coerce('y');
Expand All@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
coerce('sizing');
coerce('opacity');

for(var i = 0; i < 2; i++) {
var tdMock = { _fullLayout: fullLayout },
axLetter = ['x', 'y'][i];
var gdMock = { _fullLayout: fullLayout },
axLetters = ['x', 'y'];

for(var i = 0; i < 2; i++) {
// 'paper' is the fallback axref
Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper');
Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper');
}

return imageOut;
Expand Down
18 changes: 8 additions & 10 deletions src/components/images/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

module.exports = function draw(gd) {

var fullLayout = gd._fullLayout,
imageDataAbove = [],
imageDataSubplot = [],
imageDataBelow = [];

if(!fullLayout.images) return;


// Sort into top, subplot, and bottom layers
for(var i = 0; i < fullLayout.images.length; i++) {
var img = fullLayout.images[i];

if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
if(img.visible) {
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/shapes/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this shape is visible.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'rect', 'path', 'line'],
Expand Down
3 changes: 2 additions & 1 deletion src/components/shapes/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
Expand All@@ -17,7 +18,7 @@ var helpers = require('./helpers');

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
shapeList = fullLayout.shapes;
shapeList = Lib.filterVisible(fullLayout.shapes);

if(!shapeList.length || !gd._fullData.length) return;

Expand Down
13 changes: 9 additions & 4 deletions src/components/shapes/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,9 @@ function draw(gd) {
fullLayout._shapeSubplotLayer.selectAll('path').remove();

for(var i = 0; i < fullLayout.shapes.length; i++) {
drawOne(gd, i);
if(fullLayout.shapes[i].visible) {
drawOne(gd, i);
}
}

// may need to resurrect this if we put text (LaTeX) in shapes
Expand DownExpand Up@@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) {
// TODO: clean this up and remove it.
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input shape as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

var posAttrs = ['x0', 'x1', 'y0', 'y1'];
// return early in visible: false updates
if(optionsIn.visible === false) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
posAttrs = ['x0', 'x1', 'y0', 'y1'];

for(i = 0; i < 4; i++) {
var posAttr = posAttrs[i];
// if we don't have an explicit position already,
Expand Down
4 changes: 4 additions & 0 deletions src/components/shapes/shape_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return shapeOut;

coerce('layer');
coerce('opacity');
coerce('fillcolor');
Expand Down
19 changes: 13 additions & 6 deletions src/lib/filter_visible.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,14 +9,21 @@

'use strict';

module.exports = function filterVisible(dataIn) {
var dataOut = [];
/** Filter out object items with visible !== true
* insider array container.
*
* @param {array of objects} container
* @return {array of objects} of length <= container
*
*/
module.exports = function filterVisible(container) {
var out = [];

for(var i = 0; i < dataIn.length; i++) {
var trace = dataIn[i];
for(var i = 0; i < container.length; i++) {
var item = container[i];

if(trace.visible === true) dataOut.push(trace);
if(item.visible === true) out.push(item);
}

return dataOut;
return out;
};
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
lib.notifier = require('./notifier');

lib.filterUnique = require('./filter_unique');
lib.filterVisible = require('./filter_visible');


/**
* swap x and y of the same attribute in container cont
Expand Down
7 changes: 4 additions & 3 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
objList = layout[objType] || [],
obji = objList[objNum] || {};

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

// if p.parts is just an annotation number, and val is either
// 'add' or an entire annotation to add, the undo is 'remove'
// if val is 'remove' then undo is the whole annotation object
if(p.parts.length === 2) {

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
undoit[ai] = 'remove';
}
Expand Down
3 changes: 1 addition & 2 deletions src/plots/ternary/ternary.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
var setConvert = require('../cartesian/set_convert');
var extendFlat = require('../../lib/extend').extendFlat;
var Axes = require('../cartesian/axes');
var filterVisible = require('../../lib/filter_visible');
var dragElement = require('../../components/dragelement');
var Titles = require('../../components/titles');
var prepSelect = require('../cartesian/select');
Expand DownExpand Up@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
var moduleData = traceHash[moduleNames[i]];
var _module = moduleData[0]._module;

_module.plot(_this, filterVisible(moduleData), ternaryLayout);
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
}

_this.traceHash = traceHash;
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/annotations.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
{"text":"opacity","opacity":0.5,"x":5,"y":5},
{"text":"not-visible", "visible": false},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock is not autoranged... do you want to also include an invisible annotation in annotations-autorange.json, positioned to verify that it doesn't contribute? Same for shapes.

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to also include an invisible annotation in annotations-autorange.json

done in a2dd634

Same for shapes.

the shape_below_traces is autoranged, so that's a ✅ already

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

I'd say that's a 🐛

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shape_below_traces is autoranged, so that's a ✅ already

but the shape you added there just says {visible: false} so it will get auto coordinates too, which are going to be within the range chosen by autorange anyway, ie including it in autorange wouldn't make any difference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(actually that's true of a2dd634 too right?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson good 👁️

I chose to revert a2dd634 and test annotations and shapes autorange using jasmine tests in 5e62c08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the new tests look great. Definitely better to do this with jasmine and relayout.

{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/layout_image.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,19 @@
"opacity": 0.4,
"layer": "below"
},
{
"visible": false,
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
"xref": "x",
"yref": "y",
"x": 1,
"y": 3,
"sizex": 2,
"sizey": 2,
"sizing": "stretch",
"opacity": 0.4,
"layer": "below"
},
{
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
"xref": "x",
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/shapes_below_traces.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,6 +91,7 @@
"y1": 1,
"yref": "paper"
},
{ "visible": false },
{
"fillcolor": "#f6e8c3",
"layer": "below",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `visible` attribute to layout container items. by etpinard · Pull Request #1110 · plotly/plotly.js · GitHub
Skip to content
4 changes: 4 additions & 0 deletions src/components/annotations/annotation_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return annOut;

coerce('opacity');
coerce('align');
coerce('bgcolor');
Expand Down
9 changes: 9 additions & 0 deletions src/components/annotations/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this annotation is visible.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ var draw = require('./draw').draw;

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = fullLayout.annotations;
annotationList = Lib.filterVisible(fullLayout.annotations);

if(!annotationList.length || !gd._fullData.length) return;

Expand Down
10 changes: 7 additions & 3 deletions src/components/annotations/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,9 @@ function draw(gd) {
fullLayout._infolayer.selectAll('.annotation').remove();

for(var i = 0; i < fullLayout.annotations.length; i++) {
drawOne(gd, i);
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}

return Plots.previousPromises(gd);
Expand DownExpand Up@@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) {
// where we fail here when they add/remove annotations
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input annotation as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

// return early in visible: false updates
if(optionsIn.visible === false) return;

var gs = fullLayout._size;
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

var axLetters = ['x', 'y'];
for(i = 0; i < 2; i++) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/images/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this image is visible.'
].join(' ')
},

source: {
valType: 'string',
role: 'info',
Expand Down
36 changes: 17 additions & 19 deletions src/components/images/defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var attributes = require('./attributes');

var name = 'images';

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [],
contOut = layoutOut[name] = [];

if(!layoutIn.images || !Array.isArray(layoutIn.images)) return;
for(var i = 0; i < contIn.length; i++) {
var itemIn = contIn[i] || {},
itemOut = {};

imageDefaults(itemIn, itemOut, layoutOut);

var containerIn = layoutIn.images,
containerOut = layoutOut.images = [];


for(var i = 0; i < containerIn.length; i++) {
var image = containerIn[i];

if(!image.source) continue;

var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
containerOut.push(defaulted);
contOut.push(itemOut);
}
};


function imageDefaults(imageIn, imageOut, fullLayout) {

imageOut = imageOut || {};

function coerce(attr, dflt) {
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
}

coerce('source');
var source = coerce('source');
var visible = coerce('visible', !!source);

if(!visible) return imageOut;

coerce('layer');
coerce('x');
coerce('y');
Expand All@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
coerce('sizing');
coerce('opacity');

for(var i = 0; i < 2; i++) {
var tdMock = { _fullLayout: fullLayout },
axLetter = ['x', 'y'][i];
var gdMock = { _fullLayout: fullLayout },
axLetters = ['x', 'y'];

for(var i = 0; i < 2; i++) {
// 'paper' is the fallback axref
Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper');
Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper');
}

return imageOut;
Expand Down
18 changes: 8 additions & 10 deletions src/components/images/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

module.exports = function draw(gd) {

var fullLayout = gd._fullLayout,
imageDataAbove = [],
imageDataSubplot = [],
imageDataBelow = [];

if(!fullLayout.images) return;


// Sort into top, subplot, and bottom layers
for(var i = 0; i < fullLayout.images.length; i++) {
var img = fullLayout.images[i];

if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
if(img.visible) {
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/shapes/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this shape is visible.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'rect', 'path', 'line'],
Expand Down
3 changes: 2 additions & 1 deletion src/components/shapes/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
Expand All@@ -17,7 +18,7 @@ var helpers = require('./helpers');

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
shapeList = fullLayout.shapes;
shapeList = Lib.filterVisible(fullLayout.shapes);

if(!shapeList.length || !gd._fullData.length) return;

Expand Down
13 changes: 9 additions & 4 deletions src/components/shapes/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,9 @@ function draw(gd) {
fullLayout._shapeSubplotLayer.selectAll('path').remove();

for(var i = 0; i < fullLayout.shapes.length; i++) {
drawOne(gd, i);
if(fullLayout.shapes[i].visible) {
drawOne(gd, i);
}
}

// may need to resurrect this if we put text (LaTeX) in shapes
Expand DownExpand Up@@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) {
// TODO: clean this up and remove it.
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input shape as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

var posAttrs = ['x0', 'x1', 'y0', 'y1'];
// return early in visible: false updates
if(optionsIn.visible === false) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
posAttrs = ['x0', 'x1', 'y0', 'y1'];

for(i = 0; i < 4; i++) {
var posAttr = posAttrs[i];
// if we don't have an explicit position already,
Expand Down
4 changes: 4 additions & 0 deletions src/components/shapes/shape_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return shapeOut;

coerce('layer');
coerce('opacity');
coerce('fillcolor');
Expand Down
19 changes: 13 additions & 6 deletions src/lib/filter_visible.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,14 +9,21 @@

'use strict';

module.exports = function filterVisible(dataIn) {
var dataOut = [];
/** Filter out object items with visible !== true
* insider array container.
*
* @param {array of objects} container
* @return {array of objects} of length <= container
*
*/
module.exports = function filterVisible(container) {
var out = [];

for(var i = 0; i < dataIn.length; i++) {
var trace = dataIn[i];
for(var i = 0; i < container.length; i++) {
var item = container[i];

if(trace.visible === true) dataOut.push(trace);
if(item.visible === true) out.push(item);
}

return dataOut;
return out;
};
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
lib.notifier = require('./notifier');

lib.filterUnique = require('./filter_unique');
lib.filterVisible = require('./filter_visible');


/**
* swap x and y of the same attribute in container cont
Expand Down
7 changes: 4 additions & 3 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
objList = layout[objType] || [],
obji = objList[objNum] || {};

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

// if p.parts is just an annotation number, and val is either
// 'add' or an entire annotation to add, the undo is 'remove'
// if val is 'remove' then undo is the whole annotation object
if(p.parts.length === 2) {

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
undoit[ai] = 'remove';
}
Expand Down
3 changes: 1 addition & 2 deletions src/plots/ternary/ternary.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
var setConvert = require('../cartesian/set_convert');
var extendFlat = require('../../lib/extend').extendFlat;
var Axes = require('../cartesian/axes');
var filterVisible = require('../../lib/filter_visible');
var dragElement = require('../../components/dragelement');
var Titles = require('../../components/titles');
var prepSelect = require('../cartesian/select');
Expand DownExpand Up@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
var moduleData = traceHash[moduleNames[i]];
var _module = moduleData[0]._module;

_module.plot(_this, filterVisible(moduleData), ternaryLayout);
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
}

_this.traceHash = traceHash;
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/annotations.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
{"text":"opacity","opacity":0.5,"x":5,"y":5},
{"text":"not-visible", "visible": false},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock is not autoranged... do you want to also include an invisible annotation in annotations-autorange.json, positioned to verify that it doesn't contribute? Same for shapes.

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to also include an invisible annotation in annotations-autorange.json

done in a2dd634

Same for shapes.

the shape_below_traces is autoranged, so that's a ✅ already

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

I'd say that's a 🐛

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shape_below_traces is autoranged, so that's a ✅ already

but the shape you added there just says {visible: false} so it will get auto coordinates too, which are going to be within the range chosen by autorange anyway, ie including it in autorange wouldn't make any difference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(actually that's true of a2dd634 too right?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson good 👁️

I chose to revert a2dd634 and test annotations and shapes autorange using jasmine tests in 5e62c08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the new tests look great. Definitely better to do this with jasmine and relayout.

{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/layout_image.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,19 @@
"opacity": 0.4,
"layer": "below"
},
{
"visible": false,
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
"xref": "x",
"yref": "y",
"x": 1,
"y": 3,
"sizex": 2,
"sizey": 2,
"sizing": "stretch",
"opacity": 0.4,
"layer": "below"
},
{
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
"xref": "x",
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/shapes_below_traces.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,6 +91,7 @@
"y1": 1,
"yref": "paper"
},
{ "visible": false },
{
"fillcolor": "#f6e8c3",
"layer": "below",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add `visible` attribute to layout container items. by etpinard · Pull Request #1110 · plotly/plotly.js · GitHub
Skip to content
4 changes: 4 additions & 0 deletions src/components/annotations/annotation_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return annOut;

coerce('opacity');
coerce('align');
coerce('bgcolor');
Expand Down
9 changes: 9 additions & 0 deletions src/components/annotations/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this annotation is visible.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ var draw = require('./draw').draw;

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = fullLayout.annotations;
annotationList = Lib.filterVisible(fullLayout.annotations);

if(!annotationList.length || !gd._fullData.length) return;

Expand Down
10 changes: 7 additions & 3 deletions src/components/annotations/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,9 @@ function draw(gd) {
fullLayout._infolayer.selectAll('.annotation').remove();

for(var i = 0; i < fullLayout.annotations.length; i++) {
drawOne(gd, i);
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}

return Plots.previousPromises(gd);
Expand DownExpand Up@@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) {
// where we fail here when they add/remove annotations
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input annotation as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

// return early in visible: false updates
if(optionsIn.visible === false) return;

var gs = fullLayout._size;
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

var axLetters = ['x', 'y'];
for(i = 0; i < 2; i++) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/images/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this image is visible.'
].join(' ')
},

source: {
valType: 'string',
role: 'info',
Expand Down
36 changes: 17 additions & 19 deletions src/components/images/defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var attributes = require('./attributes');

var name = 'images';

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [],
contOut = layoutOut[name] = [];

if(!layoutIn.images || !Array.isArray(layoutIn.images)) return;
for(var i = 0; i < contIn.length; i++) {
var itemIn = contIn[i] || {},
itemOut = {};

imageDefaults(itemIn, itemOut, layoutOut);

var containerIn = layoutIn.images,
containerOut = layoutOut.images = [];


for(var i = 0; i < containerIn.length; i++) {
var image = containerIn[i];

if(!image.source) continue;

var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
containerOut.push(defaulted);
contOut.push(itemOut);
}
};


function imageDefaults(imageIn, imageOut, fullLayout) {

imageOut = imageOut || {};

function coerce(attr, dflt) {
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
}

coerce('source');
var source = coerce('source');
var visible = coerce('visible', !!source);

if(!visible) return imageOut;

coerce('layer');
coerce('x');
coerce('y');
Expand All@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
coerce('sizing');
coerce('opacity');

for(var i = 0; i < 2; i++) {
var tdMock = { _fullLayout: fullLayout },
axLetter = ['x', 'y'][i];
var gdMock = { _fullLayout: fullLayout },
axLetters = ['x', 'y'];

for(var i = 0; i < 2; i++) {
// 'paper' is the fallback axref
Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper');
Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper');
}

return imageOut;
Expand Down
18 changes: 8 additions & 10 deletions src/components/images/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

module.exports = function draw(gd) {

var fullLayout = gd._fullLayout,
imageDataAbove = [],
imageDataSubplot = [],
imageDataBelow = [];

if(!fullLayout.images) return;


// Sort into top, subplot, and bottom layers
for(var i = 0; i < fullLayout.images.length; i++) {
var img = fullLayout.images[i];

if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
if(img.visible) {
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/shapes/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this shape is visible.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'rect', 'path', 'line'],
Expand Down
3 changes: 2 additions & 1 deletion src/components/shapes/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
Expand All@@ -17,7 +18,7 @@ var helpers = require('./helpers');

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
shapeList = fullLayout.shapes;
shapeList = Lib.filterVisible(fullLayout.shapes);

if(!shapeList.length || !gd._fullData.length) return;

Expand Down
13 changes: 9 additions & 4 deletions src/components/shapes/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,9 @@ function draw(gd) {
fullLayout._shapeSubplotLayer.selectAll('path').remove();

for(var i = 0; i < fullLayout.shapes.length; i++) {
drawOne(gd, i);
if(fullLayout.shapes[i].visible) {
drawOne(gd, i);
}
}

// may need to resurrect this if we put text (LaTeX) in shapes
Expand DownExpand Up@@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) {
// TODO: clean this up and remove it.
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input shape as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

var posAttrs = ['x0', 'x1', 'y0', 'y1'];
// return early in visible: false updates
if(optionsIn.visible === false) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
posAttrs = ['x0', 'x1', 'y0', 'y1'];

for(i = 0; i < 4; i++) {
var posAttr = posAttrs[i];
// if we don't have an explicit position already,
Expand Down
4 changes: 4 additions & 0 deletions src/components/shapes/shape_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return shapeOut;

coerce('layer');
coerce('opacity');
coerce('fillcolor');
Expand Down
19 changes: 13 additions & 6 deletions src/lib/filter_visible.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,14 +9,21 @@

'use strict';

module.exports = function filterVisible(dataIn) {
var dataOut = [];
/** Filter out object items with visible !== true
* insider array container.
*
* @param {array of objects} container
* @return {array of objects} of length <= container
*
*/
module.exports = function filterVisible(container) {
var out = [];

for(var i = 0; i < dataIn.length; i++) {
var trace = dataIn[i];
for(var i = 0; i < container.length; i++) {
var item = container[i];

if(trace.visible === true) dataOut.push(trace);
if(item.visible === true) out.push(item);
}

return dataOut;
return out;
};
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
lib.notifier = require('./notifier');

lib.filterUnique = require('./filter_unique');
lib.filterVisible = require('./filter_visible');


/**
* swap x and y of the same attribute in container cont
Expand Down
7 changes: 4 additions & 3 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
objList = layout[objType] || [],
obji = objList[objNum] || {};

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

// if p.parts is just an annotation number, and val is either
// 'add' or an entire annotation to add, the undo is 'remove'
// if val is 'remove' then undo is the whole annotation object
if(p.parts.length === 2) {

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
undoit[ai] = 'remove';
}
Expand Down
3 changes: 1 addition & 2 deletions src/plots/ternary/ternary.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
var setConvert = require('../cartesian/set_convert');
var extendFlat = require('../../lib/extend').extendFlat;
var Axes = require('../cartesian/axes');
var filterVisible = require('../../lib/filter_visible');
var dragElement = require('../../components/dragelement');
var Titles = require('../../components/titles');
var prepSelect = require('../cartesian/select');
Expand DownExpand Up@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
var moduleData = traceHash[moduleNames[i]];
var _module = moduleData[0]._module;

_module.plot(_this, filterVisible(moduleData), ternaryLayout);
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
}

_this.traceHash = traceHash;
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/annotations.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
{"text":"opacity","opacity":0.5,"x":5,"y":5},
{"text":"not-visible", "visible": false},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock is not autoranged... do you want to also include an invisible annotation in annotations-autorange.json, positioned to verify that it doesn't contribute? Same for shapes.

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to also include an invisible annotation in annotations-autorange.json

done in a2dd634

Same for shapes.

the shape_below_traces is autoranged, so that's a ✅ already

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

I'd say that's a 🐛

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shape_below_traces is autoranged, so that's a ✅ already

but the shape you added there just says {visible: false} so it will get auto coordinates too, which are going to be within the range chosen by autorange anyway, ie including it in autorange wouldn't make any difference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(actually that's true of a2dd634 too right?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson good 👁️

I chose to revert a2dd634 and test annotations and shapes autorange using jasmine tests in 5e62c08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the new tests look great. Definitely better to do this with jasmine and relayout.

{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/layout_image.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,19 @@
"opacity": 0.4,
"layer": "below"
},
{
"visible": false,
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
"xref": "x",
"yref": "y",
"x": 1,
"y": 3,
"sizex": 2,
"sizey": 2,
"sizing": "stretch",
"opacity": 0.4,
"layer": "below"
},
{
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
"xref": "x",
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/shapes_below_traces.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,6 +91,7 @@
"y1": 1,
"yref": "paper"
},
{ "visible": false },
{
"fillcolor": "#f6e8c3",
"layer": "below",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `visible` attribute to layout container items. by etpinard · Pull Request #1110 · plotly/plotly.js · GitHub
Skip to content
4 changes: 4 additions & 0 deletions src/components/annotations/annotation_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return annOut;

coerce('opacity');
coerce('align');
coerce('bgcolor');
Expand Down
9 changes: 9 additions & 0 deletions src/components/annotations/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this annotation is visible.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ var draw = require('./draw').draw;

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = fullLayout.annotations;
annotationList = Lib.filterVisible(fullLayout.annotations);

if(!annotationList.length || !gd._fullData.length) return;

Expand Down
10 changes: 7 additions & 3 deletions src/components/annotations/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,9 @@ function draw(gd) {
fullLayout._infolayer.selectAll('.annotation').remove();

for(var i = 0; i < fullLayout.annotations.length; i++) {
drawOne(gd, i);
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}

return Plots.previousPromises(gd);
Expand DownExpand Up@@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) {
// where we fail here when they add/remove annotations
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input annotation as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

// return early in visible: false updates
if(optionsIn.visible === false) return;

var gs = fullLayout._size;
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

var axLetters = ['x', 'y'];
for(i = 0; i < 2; i++) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/images/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this image is visible.'
].join(' ')
},

source: {
valType: 'string',
role: 'info',
Expand Down
36 changes: 17 additions & 19 deletions src/components/images/defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var attributes = require('./attributes');

var name = 'images';

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [],
contOut = layoutOut[name] = [];

if(!layoutIn.images || !Array.isArray(layoutIn.images)) return;
for(var i = 0; i < contIn.length; i++) {
var itemIn = contIn[i] || {},
itemOut = {};

imageDefaults(itemIn, itemOut, layoutOut);

var containerIn = layoutIn.images,
containerOut = layoutOut.images = [];


for(var i = 0; i < containerIn.length; i++) {
var image = containerIn[i];

if(!image.source) continue;

var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
containerOut.push(defaulted);
contOut.push(itemOut);
}
};


function imageDefaults(imageIn, imageOut, fullLayout) {

imageOut = imageOut || {};

function coerce(attr, dflt) {
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
}

coerce('source');
var source = coerce('source');
var visible = coerce('visible', !!source);

if(!visible) return imageOut;

coerce('layer');
coerce('x');
coerce('y');
Expand All@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
coerce('sizing');
coerce('opacity');

for(var i = 0; i < 2; i++) {
var tdMock = { _fullLayout: fullLayout },
axLetter = ['x', 'y'][i];
var gdMock = { _fullLayout: fullLayout },
axLetters = ['x', 'y'];

for(var i = 0; i < 2; i++) {
// 'paper' is the fallback axref
Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper');
Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper');
}

return imageOut;
Expand Down
18 changes: 8 additions & 10 deletions src/components/images/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

module.exports = function draw(gd) {

var fullLayout = gd._fullLayout,
imageDataAbove = [],
imageDataSubplot = [],
imageDataBelow = [];

if(!fullLayout.images) return;


// Sort into top, subplot, and bottom layers
for(var i = 0; i < fullLayout.images.length; i++) {
var img = fullLayout.images[i];

if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
if(img.visible) {
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/shapes/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this shape is visible.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'rect', 'path', 'line'],
Expand Down
3 changes: 2 additions & 1 deletion src/components/shapes/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
Expand All@@ -17,7 +18,7 @@ var helpers = require('./helpers');

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
shapeList = fullLayout.shapes;
shapeList = Lib.filterVisible(fullLayout.shapes);

if(!shapeList.length || !gd._fullData.length) return;

Expand Down
13 changes: 9 additions & 4 deletions src/components/shapes/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,9 @@ function draw(gd) {
fullLayout._shapeSubplotLayer.selectAll('path').remove();

for(var i = 0; i < fullLayout.shapes.length; i++) {
drawOne(gd, i);
if(fullLayout.shapes[i].visible) {
drawOne(gd, i);
}
}

// may need to resurrect this if we put text (LaTeX) in shapes
Expand DownExpand Up@@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) {
// TODO: clean this up and remove it.
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input shape as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

var posAttrs = ['x0', 'x1', 'y0', 'y1'];
// return early in visible: false updates
if(optionsIn.visible === false) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
posAttrs = ['x0', 'x1', 'y0', 'y1'];

for(i = 0; i < 4; i++) {
var posAttr = posAttrs[i];
// if we don't have an explicit position already,
Expand Down
4 changes: 4 additions & 0 deletions src/components/shapes/shape_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return shapeOut;

coerce('layer');
coerce('opacity');
coerce('fillcolor');
Expand Down
19 changes: 13 additions & 6 deletions src/lib/filter_visible.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,14 +9,21 @@

'use strict';

module.exports = function filterVisible(dataIn) {
var dataOut = [];
/** Filter out object items with visible !== true
* insider array container.
*
* @param {array of objects} container
* @return {array of objects} of length <= container
*
*/
module.exports = function filterVisible(container) {
var out = [];

for(var i = 0; i < dataIn.length; i++) {
var trace = dataIn[i];
for(var i = 0; i < container.length; i++) {
var item = container[i];

if(trace.visible === true) dataOut.push(trace);
if(item.visible === true) out.push(item);
}

return dataOut;
return out;
};
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
lib.notifier = require('./notifier');

lib.filterUnique = require('./filter_unique');
lib.filterVisible = require('./filter_visible');


/**
* swap x and y of the same attribute in container cont
Expand Down
7 changes: 4 additions & 3 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
objList = layout[objType] || [],
obji = objList[objNum] || {};

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

// if p.parts is just an annotation number, and val is either
// 'add' or an entire annotation to add, the undo is 'remove'
// if val is 'remove' then undo is the whole annotation object
if(p.parts.length === 2) {

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
undoit[ai] = 'remove';
}
Expand Down
3 changes: 1 addition & 2 deletions src/plots/ternary/ternary.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
var setConvert = require('../cartesian/set_convert');
var extendFlat = require('../../lib/extend').extendFlat;
var Axes = require('../cartesian/axes');
var filterVisible = require('../../lib/filter_visible');
var dragElement = require('../../components/dragelement');
var Titles = require('../../components/titles');
var prepSelect = require('../cartesian/select');
Expand DownExpand Up@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
var moduleData = traceHash[moduleNames[i]];
var _module = moduleData[0]._module;

_module.plot(_this, filterVisible(moduleData), ternaryLayout);
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
}

_this.traceHash = traceHash;
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/annotations.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
{"text":"opacity","opacity":0.5,"x":5,"y":5},
{"text":"not-visible", "visible": false},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock is not autoranged... do you want to also include an invisible annotation in annotations-autorange.json, positioned to verify that it doesn't contribute? Same for shapes.

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to also include an invisible annotation in annotations-autorange.json

done in a2dd634

Same for shapes.

the shape_below_traces is autoranged, so that's a ✅ already

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

I'd say that's a 🐛

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shape_below_traces is autoranged, so that's a ✅ already

but the shape you added there just says {visible: false} so it will get auto coordinates too, which are going to be within the range chosen by autorange anyway, ie including it in autorange wouldn't make any difference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(actually that's true of a2dd634 too right?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson good 👁️

I chose to revert a2dd634 and test annotations and shapes autorange using jasmine tests in 5e62c08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the new tests look great. Definitely better to do this with jasmine and relayout.

{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/layout_image.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,19 @@
"opacity": 0.4,
"layer": "below"
},
{
"visible": false,
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
"xref": "x",
"yref": "y",
"x": 1,
"y": 3,
"sizex": 2,
"sizey": 2,
"sizing": "stretch",
"opacity": 0.4,
"layer": "below"
},
{
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
"xref": "x",
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/shapes_below_traces.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,6 +91,7 @@
"y1": 1,
"yref": "paper"
},
{ "visible": false },
{
"fillcolor": "#f6e8c3",
"layer": "below",
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add `visible` attribute to layout container items. by etpinard · Pull Request #1110 · plotly/plotly.js · GitHub
Skip to content
4 changes: 4 additions & 0 deletions src/components/annotations/annotation_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return annOut;

coerce('opacity');
coerce('align');
coerce('bgcolor');
Expand Down
9 changes: 9 additions & 0 deletions src/components/annotations/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this annotation is visible.'
].join(' ')
},

text: {
valType: 'string',
role: 'info',
Expand Down
2 changes: 1 addition & 1 deletion src/components/annotations/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ var draw = require('./draw').draw;

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
annotationList = fullLayout.annotations;
annotationList = Lib.filterVisible(fullLayout.annotations);

if(!annotationList.length || !gd._fullData.length) return;

Expand Down
10 changes: 7 additions & 3 deletions src/components/annotations/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,9 @@ function draw(gd) {
fullLayout._infolayer.selectAll('.annotation').remove();

for(var i = 0; i < fullLayout.annotations.length; i++) {
drawOne(gd, i);
if(fullLayout.annotations[i].visible) {
drawOne(gd, i);
}
}

return Plots.previousPromises(gd);
Expand DownExpand Up@@ -140,8 +142,6 @@ function drawOne(gd, index, opt, value) {
// where we fail here when they add/remove annotations
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input annotation as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

// return early in visible: false updates
if(optionsIn.visible === false) return;

var gs = fullLayout._size;
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

var axLetters = ['x', 'y'];
for(i = 0; i < 2; i++) {
Expand Down
9 changes: 9 additions & 0 deletions src/components/images/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this image is visible.'
].join(' ')
},

source: {
valType: 'string',
role: 'info',
Expand Down
36 changes: 17 additions & 19 deletions src/components/images/defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
var Lib = require('../../lib');
var attributes = require('./attributes');

var name = 'images';

module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
var contIn = Array.isArray(layoutIn[name]) ? layoutIn[name] : [],
contOut = layoutOut[name] = [];

if(!layoutIn.images || !Array.isArray(layoutIn.images)) return;
for(var i = 0; i < contIn.length; i++) {
var itemIn = contIn[i] || {},
itemOut = {};

imageDefaults(itemIn, itemOut, layoutOut);

var containerIn = layoutIn.images,
containerOut = layoutOut.images = [];


for(var i = 0; i < containerIn.length; i++) {
var image = containerIn[i];

if(!image.source) continue;

var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
containerOut.push(defaulted);
contOut.push(itemOut);
}
};


function imageDefaults(imageIn, imageOut, fullLayout) {

imageOut = imageOut || {};

function coerce(attr, dflt) {
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
}

coerce('source');
var source = coerce('source');
var visible = coerce('visible', !!source);

if(!visible) return imageOut;

coerce('layer');
coerce('x');
coerce('y');
Expand All@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
coerce('sizing');
coerce('opacity');

for(var i = 0; i < 2; i++) {
var tdMock = { _fullLayout: fullLayout },
axLetter = ['x', 'y'][i];
var gdMock = { _fullLayout: fullLayout },
axLetters = ['x', 'y'];

for(var i = 0; i < 2; i++) {
// 'paper' is the fallback axref
Axes.coerceRef(imageIn, imageOut, tdMock, axLetter, 'paper');
Axes.coerceRef(imageIn, imageOut, gdMock, axLetters[i], 'paper');
}

return imageOut;
Expand Down
18 changes: 8 additions & 10 deletions src/components/images/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');

module.exports = function draw(gd) {

var fullLayout = gd._fullLayout,
imageDataAbove = [],
imageDataSubplot = [],
imageDataBelow = [];

if(!fullLayout.images) return;


// Sort into top, subplot, and bottom layers
for(var i = 0; i < fullLayout.images.length; i++) {
var img = fullLayout.images[i];

if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
if(img.visible) {
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
imageDataSubplot.push(img);
} else if(img.layer === 'above') {
imageDataAbove.push(img);
} else {
imageDataBelow.push(img);
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/components/shapes/attributes.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
module.exports = {
_isLinkedToArray: true,

visible: {
valType: 'boolean',
role: 'info',
dflt: true,
description: [
'Determines whether or not this shape is visible.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'rect', 'path', 'line'],
Expand Down
3 changes: 2 additions & 1 deletion src/components/shapes/calc_autorange.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@

'use strict';

var Lib = require('../../lib');
var Axes = require('../../plots/cartesian/axes');

var constants = require('./constants');
Expand All@@ -17,7 +18,7 @@ var helpers = require('./helpers');

module.exports = function calcAutorange(gd) {
var fullLayout = gd._fullLayout,
shapeList = fullLayout.shapes;
shapeList = Lib.filterVisible(fullLayout.shapes);

if(!shapeList.length || !gd._fullData.length) return;

Expand Down
13 changes: 9 additions & 4 deletions src/components/shapes/draw.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,7 +49,9 @@ function draw(gd) {
fullLayout._shapeSubplotLayer.selectAll('path').remove();

for(var i = 0; i < fullLayout.shapes.length; i++) {
drawOne(gd, i);
if(fullLayout.shapes[i].visible) {
drawOne(gd, i);
}
}

// may need to resurrect this if we put text (LaTeX) in shapes
Expand DownExpand Up@@ -169,8 +171,6 @@ function updateShape(gd, index, opt, value) {
// TODO: clean this up and remove it.
if(!optionsIn) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};

// alter the input shape as requested
var optionsEdit = {};
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
Expand All@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
}

var posAttrs = ['x0', 'x1', 'y0', 'y1'];
// return early in visible: false updates
if(optionsIn.visible === false) return;

var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
posAttrs = ['x0', 'x1', 'y0', 'y1'];

for(i = 0; i < 4; i++) {
var posAttr = posAttrs[i];
// if we don't have an explicit position already,
Expand Down
4 changes: 4 additions & 0 deletions src/components/shapes/shape_defaults.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
}

var visible = coerce('visible');

if(!visible) return shapeOut;

coerce('layer');
coerce('opacity');
coerce('fillcolor');
Expand Down
19 changes: 13 additions & 6 deletions src/lib/filter_visible.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,14 +9,21 @@

'use strict';

module.exports = function filterVisible(dataIn) {
var dataOut = [];
/** Filter out object items with visible !== true
* insider array container.
*
* @param {array of objects} container
* @return {array of objects} of length <= container
*
*/
module.exports = function filterVisible(container) {
var out = [];

for(var i = 0; i < dataIn.length; i++) {
var trace = dataIn[i];
for(var i = 0; i < container.length; i++) {
var item = container[i];

if(trace.visible === true) dataOut.push(trace);
if(item.visible === true) out.push(item);
}

return dataOut;
return out;
};
2 changes: 2 additions & 0 deletions src/lib/index.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
lib.notifier = require('./notifier');

lib.filterUnique = require('./filter_unique');
lib.filterVisible = require('./filter_visible');


/**
* swap x and y of the same attribute in container cont
Expand Down
7 changes: 4 additions & 3 deletions src/plot_api/plot_api.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
objList = layout[objType] || [],
obji = objList[objNum] || {};

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

// if p.parts is just an annotation number, and val is either
// 'add' or an entire annotation to add, the undo is 'remove'
// if val is 'remove' then undo is the whole annotation object
if(p.parts.length === 2) {

// new API, remove annotation / shape with `null`
if(vi === null) aobj[ai] = 'remove';

if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
undoit[ai] = 'remove';
}
Expand Down
3 changes: 1 addition & 2 deletions src/plots/ternary/ternary.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
var setConvert = require('../cartesian/set_convert');
var extendFlat = require('../../lib/extend').extendFlat;
var Axes = require('../cartesian/axes');
var filterVisible = require('../../lib/filter_visible');
var dragElement = require('../../components/dragelement');
var Titles = require('../../components/titles');
var prepSelect = require('../cartesian/select');
Expand DownExpand Up@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
var moduleData = traceHash[moduleNames[i]];
var _module = moduleData[0]._module;

_module.plot(_this, filterVisible(moduleData), ternaryLayout);
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
}

_this.traceHash = traceHash;
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/annotations.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
{"text":"opacity","opacity":0.5,"x":5,"y":5},
{"text":"not-visible", "visible": false},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This mock is not autoranged... do you want to also include an invisible annotation in annotations-autorange.json, positioned to verify that it doesn't contribute? Same for shapes.

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to also include an invisible annotation in annotations-autorange.json

done in a2dd634

Same for shapes.

the shape_below_traces is autoranged, so that's a ✅ already

I guess images currently don't contribute to autorange at all - is that on purpose or should I consider it a bug?

I'd say that's a 🐛

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the shape_below_traces is autoranged, so that's a ✅ already

but the shape you added there just says {visible: false} so it will get auto coordinates too, which are going to be within the range chosen by autorange anyway, ie including it in autorange wouldn't make any difference.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(actually that's true of a2dd634 too right?)

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcjohnson good 👁️

I chose to revert a2dd634 and test annotations and shapes autorange using jasmine tests in 5e62c08

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, the new tests look great. Definitely better to do this with jasmine and relayout.

{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},
Expand Down
13 changes: 13 additions & 0 deletions test/image/mocks/layout_image.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,6 +51,19 @@
"opacity": 0.4,
"layer": "below"
},
{
"visible": false,
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
"xref": "x",
"yref": "y",
"x": 1,
"y": 3,
"sizex": 2,
"sizey": 2,
"sizing": "stretch",
"opacity": 0.4,
"layer": "below"
},
{
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
"xref": "x",
Expand Down
1 change: 1 addition & 0 deletions test/image/mocks/shapes_below_traces.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -91,6 +91,7 @@
"y1": 1,
"yref": "paper"
},
{ "visible": false },
{
"fillcolor": "#f6e8c3",
"layer": "below",
Expand Down
Loading