Skip to content

Commit 38aa4b3

Browse files
authored
Merge pull request #1110 from plotly/annotations-shape-visible
Add `visible` attribute to layout container items.
2 parents 8745a22 + 328a6ca commit 38aa4b3

21 files changed

+332
-63
lines changed

src/components/annotations/annotation_defaults.js

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ module.exports = function handleAnnotationDefaults(annIn, fullLayout) {
2323
return Lib.coerce(annIn, annOut, attributes, attr, dflt);
2424
}
2525

26+
var visible = coerce('visible');
27+
28+
if(!visible) return annOut;
29+
2630
coerce('opacity');
2731
coerce('align');
2832
coerce('bgcolor');

src/components/annotations/attributes.js

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ var extendFlat = require('../../lib/extend').extendFlat;
1717
module.exports = {
1818
_isLinkedToArray: true,
1919

20+
visible: {
21+
valType: 'boolean',
22+
role: 'info',
23+
dflt: true,
24+
description: [
25+
'Determines whether or not this annotation is visible.'
26+
].join(' ')
27+
},
28+
2029
text: {
2130
valType: 'string',
2231
role: 'info',

src/components/annotations/calc_autorange.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ var draw = require('./draw').draw;
1717

1818
module.exports = function calcAutorange(gd) {
1919
var fullLayout = gd._fullLayout,
20-
annotationList = fullLayout.annotations;
20+
annotationList = Lib.filterVisible(fullLayout.annotations);
2121

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

src/components/annotations/draw.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ function draw(gd) {
4747
fullLayout._infolayer.selectAll('.annotation').remove();
4848

4949
for(var i = 0; i < fullLayout.annotations.length; i++) {
50-
drawOne(gd, i);
50+
if(fullLayout.annotations[i].visible) {
51+
drawOne(gd, i);
52+
}
5153
}
5254

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

143-
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};
144-
145145
// alter the input annotation as requested
146146
var optionsEdit = {};
147147
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
@@ -153,7 +153,11 @@ function drawOne(gd, index, opt, value) {
153153
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
154154
}
155155

156+
// return early in visible: false updates
157+
if(optionsIn.visible === false) return;
158+
156159
var gs = fullLayout._size;
160+
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};
157161

158162
var axLetters = ['x', 'y'];
159163
for(i = 0; i < 2; i++) {

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

src/components/shapes/attributes.js

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ var scatterLineAttrs = scatterAttrs.line;
1717
module.exports = {
1818
_isLinkedToArray: true,
1919

20+
visible: {
21+
valType: 'boolean',
22+
role: 'info',
23+
dflt: true,
24+
description: [
25+
'Determines whether or not this shape is visible.'
26+
].join(' ')
27+
},
28+
2029
type: {
2130
valType: 'enumerated',
2231
values: ['circle', 'rect', 'path', 'line'],

src/components/shapes/calc_autorange.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
'use strict';
1111

12+
var Lib = require('../../lib');
1213
var Axes = require('../../plots/cartesian/axes');
1314

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

1819
module.exports = function calcAutorange(gd) {
1920
var fullLayout = gd._fullLayout,
20-
shapeList = fullLayout.shapes;
21+
shapeList = Lib.filterVisible(fullLayout.shapes);
2122

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

src/components/shapes/draw.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ function draw(gd) {
4949
fullLayout._shapeSubplotLayer.selectAll('path').remove();
5050

5151
for(var i = 0; i < fullLayout.shapes.length; i++) {
52-
drawOne(gd, i);
52+
if(fullLayout.shapes[i].visible) {
53+
drawOne(gd, i);
54+
}
5355
}
5456

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

172-
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref};
173-
174174
// alter the input shape as requested
175175
var optionsEdit = {};
176176
if(typeof opt === 'string' && opt) optionsEdit[opt] = value;
@@ -182,7 +182,12 @@ function updateShape(gd, index, opt, value) {
182182
Lib.nestedProperty(optionsIn, k).set(optionsEdit[k]);
183183
}
184184

185-
var posAttrs = ['x0', 'x1', 'y0', 'y1'];
185+
// return early in visible: false updates
186+
if(optionsIn.visible === false) return;
187+
188+
var oldRef = {xref: optionsIn.xref, yref: optionsIn.yref},
189+
posAttrs = ['x0', 'x1', 'y0', 'y1'];
190+
186191
for(i = 0; i < 4; i++) {
187192
var posAttr = posAttrs[i];
188193
// if we don't have an explicit position already,

src/components/shapes/shape_defaults.js

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ module.exports = function handleShapeDefaults(shapeIn, fullLayout) {
2222
return Lib.coerce(shapeIn, shapeOut, attributes, attr, dflt);
2323
}
2424

25+
var visible = coerce('visible');
26+
27+
if(!visible) return shapeOut;
28+
2529
coerce('layer');
2630
coerce('opacity');
2731
coerce('fillcolor');

src/lib/filter_visible.js

+13-6
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,21 @@
99

1010
'use strict';
1111

12-
module.exports = function filterVisible(dataIn) {
13-
var dataOut = [];
12+
/** Filter out object items with visible !== true
13+
* insider array container.
14+
*
15+
* @param {array of objects} container
16+
* @return {array of objects} of length <= container
17+
*
18+
*/
19+
module.exports = function filterVisible(container) {
20+
var out = [];
1421

15-
for(var i = 0; i < dataIn.length; i++) {
16-
var trace = dataIn[i];
22+
for(var i = 0; i < container.length; i++) {
23+
var item = container[i];
1724

18-
if(trace.visible === true) dataOut.push(trace);
25+
if(item.visible === true) out.push(item);
1926
}
2027

21-
return dataOut;
28+
return out;
2229
};

src/lib/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ lib.error = loggersModule.error;
7676
lib.notifier = require('./notifier');
7777

7878
lib.filterUnique = require('./filter_unique');
79+
lib.filterVisible = require('./filter_visible');
80+
7981

8082
/**
8183
* swap x and y of the same attribute in container cont

src/plot_api/plot_api.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1917,13 +1917,14 @@ function _relayout(gd, aobj) {
19171917
objList = layout[objType] || [],
19181918
obji = objList[objNum] || {};
19191919

1920-
// new API, remove annotation / shape with `null`
1921-
if(vi === null) aobj[ai] = 'remove';
1922-
19231920
// if p.parts is just an annotation number, and val is either
19241921
// 'add' or an entire annotation to add, the undo is 'remove'
19251922
// if val is 'remove' then undo is the whole annotation object
19261923
if(p.parts.length === 2) {
1924+
1925+
// new API, remove annotation / shape with `null`
1926+
if(vi === null) aobj[ai] = 'remove';
1927+
19271928
if(aobj[ai] === 'add' || Lib.isPlainObject(aobj[ai])) {
19281929
undoit[ai] = 'remove';
19291930
}

src/plots/ternary/ternary.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ var Drawing = require('../../components/drawing');
1919
var setConvert = require('../cartesian/set_convert');
2020
var extendFlat = require('../../lib/extend').extendFlat;
2121
var Axes = require('../cartesian/axes');
22-
var filterVisible = require('../../lib/filter_visible');
2322
var dragElement = require('../../components/dragelement');
2423
var Titles = require('../../components/titles');
2524
var prepSelect = require('../cartesian/select');
@@ -94,7 +93,7 @@ proto.plot = function(ternaryData, fullLayout) {
9493
var moduleData = traceHash[moduleNames[i]];
9594
var _module = moduleData[0]._module;
9695

97-
_module.plot(_this, filterVisible(moduleData), ternaryLayout);
96+
_module.plot(_this, Lib.filterVisible(moduleData), ternaryLayout);
9897
}
9998

10099
_this.traceHash = traceHash;

test/image/mocks/annotations.json

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
{"text":"right bottom","showarrow":false,"xref":"paper","yref":"paper","xanchor":"right","yanchor":"bottom","x":0.5,"y":1},
1818
{"text":"move with page","xref":"paper","yref":"paper","x":0.75,"y":1},
1919
{"text":"opacity","opacity":0.5,"x":5,"y":5},
20+
{"text":"not-visible", "visible": false},
2021
{"text":"left<br>justified","showarrow":false,"align":"left","x":1,"y":4},
2122
{"text":"center<br>justified","showarrow":false,"x":2,"y":4},
2223
{"text":"right<br>justified","showarrow":false,"align":"right","x":3,"y":4},

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/image/mocks/shapes_below_traces.json

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"y1": 1,
9292
"yref": "paper"
9393
},
94+
{ "visible": false },
9495
{
9596
"fillcolor": "#f6e8c3",
9697
"layer": "below",

0 commit comments

Comments
 (0)