Skip to content

Commit 78e0f55

Browse files
committed
images: add 'visible' attribute
- ... and make sure that layout.images.length === fullLayout.image.length always, i.e. item with no source are coerced to { visible: false } and skipped during the draw step.
1 parent b5bfee1 commit 78e0f55

File tree

5 files changed

+74
-37
lines changed

5 files changed

+74
-37
lines changed

src/components/images/attributes.js

+9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ var cartesianConstants = require('../../plots/cartesian/constants');
1414
module.exports = {
1515
_isLinkedToArray: true,
1616

17+
visible: {
18+
valType: 'boolean',
19+
role: 'info',
20+
dflt: true,
21+
description: [
22+
'Determines whether or not this annotation is visible.'
23+
].join(' ')
24+
},
25+
1726
source: {
1827
valType: 'string',
1928
role: 'info',

src/components/images/defaults.js

+17-19
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,34 @@ var Axes = require('../../plots/cartesian/axes');
1212
var Lib = require('../../lib');
1313
var attributes = require('./attributes');
1414

15+
var name = 'images';
1516

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

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

25+
imageDefaults(itemIn, itemOut, layoutOut);
2026

21-
var containerIn = layoutIn.images,
22-
containerOut = layoutOut.images = [];
23-
24-
25-
for(var i = 0; i < containerIn.length; i++) {
26-
var image = containerIn[i];
27-
28-
if(!image.source) continue;
29-
30-
var defaulted = imageDefaults(containerIn[i] || {}, containerOut[i] || {}, layoutOut);
31-
containerOut.push(defaulted);
27+
contOut.push(itemOut);
3228
}
3329
};
3430

3531

3632
function imageDefaults(imageIn, imageOut, fullLayout) {
3733

38-
imageOut = imageOut || {};
39-
4034
function coerce(attr, dflt) {
4135
return Lib.coerce(imageIn, imageOut, attributes, attr, dflt);
4236
}
4337

44-
coerce('source');
38+
var source = coerce('source');
39+
var visible = coerce('visible', !!source);
40+
41+
if(!visible) return imageOut;
42+
4543
coerce('layer');
4644
coerce('x');
4745
coerce('y');
@@ -52,12 +50,12 @@ function imageDefaults(imageIn, imageOut, fullLayout) {
5250
coerce('sizing');
5351
coerce('opacity');
5452

55-
for(var i = 0; i < 2; i++) {
56-
var tdMock = { _fullLayout: fullLayout },
57-
axLetter = ['x', 'y'][i];
53+
var gdMock = { _fullLayout: fullLayout },
54+
axLetters = ['x', 'y'];
5855

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

6361
return imageOut;

src/components/images/draw.js

+8-10
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,23 @@ var Axes = require('../../plots/cartesian/axes');
1414
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
1515

1616
module.exports = function draw(gd) {
17-
1817
var fullLayout = gd._fullLayout,
1918
imageDataAbove = [],
2019
imageDataSubplot = [],
2120
imageDataBelow = [];
2221

23-
if(!fullLayout.images) return;
24-
25-
2622
// Sort into top, subplot, and bottom layers
2723
for(var i = 0; i < fullLayout.images.length; i++) {
2824
var img = fullLayout.images[i];
2925

30-
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
31-
imageDataSubplot.push(img);
32-
} else if(img.layer === 'above') {
33-
imageDataAbove.push(img);
34-
} else {
35-
imageDataBelow.push(img);
26+
if(img.visible) {
27+
if(img.layer === 'below' && img.xref !== 'paper' && img.yref !== 'paper') {
28+
imageDataSubplot.push(img);
29+
} else if(img.layer === 'above') {
30+
imageDataAbove.push(img);
31+
} else {
32+
imageDataBelow.push(img);
33+
}
3634
}
3735
}
3836

test/image/mocks/layout_image.json

+13
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,19 @@
5151
"opacity": 0.4,
5252
"layer": "below"
5353
},
54+
{
55+
"visible": false,
56+
"source": "https://images.plot.ly/language-icons/api-home/python-logo.png",
57+
"xref": "x",
58+
"yref": "y",
59+
"x": 1,
60+
"y": 3,
61+
"sizex": 2,
62+
"sizey": 2,
63+
"sizing": "stretch",
64+
"opacity": 0.4,
65+
"layer": "below"
66+
},
5467
{
5568
"source": "https://images.plot.ly/language-icons/api-home/matlab-logo.png",
5669
"xref": "x",

test/jasmine/tests/layout_images_test.js

+27-8
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('Layout images', function() {
2727

2828
Images.supplyLayoutDefaults(layoutIn, layoutOut);
2929

30-
expect(layoutOut.images.length).toEqual(0);
30+
expect(layoutOut.images).toEqual([{ visible: false }]);
3131
});
3232

3333
it('should reject when not an array', function() {
@@ -40,14 +40,15 @@ describe('Layout images', function() {
4040

4141
Images.supplyLayoutDefaults(layoutIn, layoutOut);
4242

43-
expect(layoutOut.images).not.toBeDefined();
43+
expect(layoutOut.images).toEqual([]);
4444
});
4545

4646
it('should coerce the correct defaults', function() {
4747
layoutIn.images[0] = { source: jsLogo };
4848

4949
var expected = {
5050
source: jsLogo,
51+
visible: true,
5152
layer: 'above',
5253
x: 0,
5354
y: 0,
@@ -319,30 +320,48 @@ describe('Layout images', function() {
319320
assertImages(0);
320321

321322
return Plotly.relayout(gd, 'images[0]', makeImage(jsLogo, 0.1, 0.1));
322-
}).then(function() {
323+
})
324+
.then(function() {
323325
assertImages(1);
324326

325327
return Plotly.relayout(gd, 'images[1]', makeImage(pythonLogo, 0.9, 0.9));
326-
}).then(function() {
328+
})
329+
.then(function() {
327330
assertImages(2);
328331

329332
return Plotly.relayout(gd, 'images[2]', makeImage(pythonLogo, 0.2, 0.5));
330-
}).then(function() {
333+
})
334+
.then(function() {
335+
assertImages(3);
336+
expect(gd.layout.images.length).toEqual(3);
337+
338+
return Plotly.relayout(gd, 'images[1].visible', false);
339+
})
340+
.then(function() {
341+
assertImages(2);
342+
expect(gd.layout.images.length).toEqual(3);
343+
344+
return Plotly.relayout(gd, 'images[1].visible', true);
345+
})
346+
.then(function() {
331347
assertImages(3);
332348
expect(gd.layout.images.length).toEqual(3);
333349

334350
return Plotly.relayout(gd, 'images[2]', null);
335-
}).then(function() {
351+
})
352+
.then(function() {
336353
assertImages(2);
337354
expect(gd.layout.images.length).toEqual(2);
338355

339356
return Plotly.relayout(gd, 'images[1]', null);
340-
}).then(function() {
357+
})
358+
.then(function() {
341359
assertImages(1);
342360
expect(gd.layout.images.length).toEqual(1);
343361

344362
return Plotly.relayout(gd, 'images[0]', null);
345-
}).then(function() {
363+
})
364+
.then(function() {
346365
assertImages(0);
347366
expect(gd.layout.images).toEqual([]);
348367

0 commit comments

Comments
 (0)