Skip to content

Commit ae3f407

Browse files
committed
add 'coloraxis' layout attr and default logic
- reuse Colorscale.supplyDefaults - use 'cmin', 'cmax' and 'cauto' no matter the trace types plotted - keep track of color axes referenced in the traces in fullLayout._colorAxes - ignore coloraxis referenced when colorbar visuals are incompatible - tweak PlotSchema.get() for layout.colorscale / layout.coloraxis? edge cases
1 parent 5e7ed27 commit ae3f407

File tree

9 files changed

+288
-64
lines changed

9 files changed

+288
-64
lines changed

src/components/colorscale/attributes.js

+19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
'use strict';
1010

1111
var colorbarAttrs = require('../colorbar/attributes');
12+
var counterRegex = require('../../lib/regex').counter;
13+
1214
var palettes = require('./scales.js').scales;
1315
var paletteStr = Object.keys(palettes);
1416

@@ -245,5 +247,22 @@ module.exports = function colorScaleAttrs(context, opts) {
245247
attrs.colorbar = colorbarAttrs;
246248
}
247249

250+
if(!opts.noColorAxis) {
251+
attrs.coloraxis = {
252+
valType: 'subplotid',
253+
role: 'info',
254+
regex: counterRegex('coloraxis'),
255+
dflt: null,
256+
editType: 'calc',
257+
description: [
258+
'Sets a reference to a shared color axis.',
259+
'References to these shared color axes are *coloraxis*, *coloraxis2*, *coloraxis3*, etc.',
260+
'Settings for these shared color axes are set in the layout, under',
261+
'`layout.coloraxis`, `layout.coloraxis2`, etc.',
262+
'Note that multiple color scales can be linked to the same color axis.'
263+
].join(' ')
264+
};
265+
}
266+
248267
return attrs;
249268
};

src/components/colorscale/defaults.js

+51-8
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,63 @@ var hasColorbar = require('../colorbar/has_colorbar');
1515
var colorbarDefaults = require('../colorbar/defaults');
1616

1717
var isValidScale = require('./scales').isValid;
18+
var traceIs = require('../../registry').traceIs;
1819

19-
function npMaybe(cont, prefix) {
20+
function npMaybe(outerCont, prefix) {
2021
var containerStr = prefix.slice(0, prefix.length - 1);
2122
return prefix ?
22-
Lib.nestedProperty(cont, containerStr).get() || {} :
23-
cont;
23+
Lib.nestedProperty(outerCont, containerStr).get() || {} :
24+
outerCont;
2425
}
2526

26-
module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce, opts) {
27+
module.exports = function colorScaleDefaults(outerContIn, outerContOut, layout, coerce, opts) {
2728
var prefix = opts.prefix;
2829
var cLetter = opts.cLetter;
29-
var containerIn = npMaybe(traceIn, prefix);
30-
var containerOut = npMaybe(traceOut, prefix);
31-
var template = npMaybe(traceOut._template || {}, prefix) || {};
30+
var inTrace = '_module' in outerContOut;
31+
var containerIn = npMaybe(outerContIn, prefix);
32+
var containerOut = npMaybe(outerContOut, prefix);
33+
var template = npMaybe(outerContOut._template || {}, prefix) || {};
34+
35+
// colorScaleDefaults wrapper called if-ever we need to reset the colorscale
36+
// attributes for containers that were linked to invalid color axes
37+
var thisFn = function() {
38+
delete outerContIn.coloraxis;
39+
delete outerContOut.coloraxis;
40+
return colorScaleDefaults(outerContIn, outerContOut, layout, coerce, opts);
41+
};
42+
43+
if(inTrace) {
44+
var colorAxes = layout._colorAxes || {};
45+
var colorAx = coerce(prefix + 'coloraxis');
46+
47+
if(colorAx) {
48+
var colorbarVisuals = (
49+
traceIs(outerContOut, 'contour') &&
50+
Lib.nestedProperty(outerContOut, 'contours.coloring').get()
51+
) || 'heatmap';
52+
53+
var stash = colorAxes[colorAx];
54+
55+
if(stash) {
56+
stash[2].push(thisFn);
57+
58+
if(stash[0] !== colorbarVisuals) {
59+
stash[0] = false;
60+
Lib.warn([
61+
'Ignoring coloraxis:', colorAx, 'setting',
62+
'as it is linked to incompatible colorscales.'
63+
].join(' '));
64+
}
65+
} else {
66+
// stash:
67+
// - colorbar visual 'type'
68+
// - colorbar options to help in Colorbar.draw
69+
// - list of colorScaleDefaults wrapper functions
70+
colorAxes[colorAx] = [colorbarVisuals, outerContOut, [thisFn]];
71+
}
72+
return;
73+
}
74+
}
3275

3376
var minIn = containerIn[cLetter + 'min'];
3477
var maxIn = containerIn[cLetter + 'max'];
@@ -58,7 +101,7 @@ module.exports = function colorScaleDefaults(traceIn, traceOut, layout, coerce,
58101
// handles both the trace case where the dflt is listed in attributes and
59102
// the marker case where the dflt is determined by hasColorbar
60103
var showScaleDflt;
61-
if(prefix) showScaleDflt = hasColorbar(containerIn);
104+
if(prefix && inTrace) showScaleDflt = hasColorbar(containerIn);
62105

63106
var showScale = coerce(prefix + 'showscale', showScaleDflt);
64107
if(showScale) colorbarDefaults(containerIn, containerOut, layout);

src/components/colorscale/layout_attributes.js

+48-25
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,63 @@
88

99
'use strict';
1010

11+
var extendFlat = require('../../lib/extend').extendFlat;
12+
13+
var colorScaleAttrs = require('./attributes');
1114
var scales = require('./scales').scales;
1215

1316
var msg = 'Note that `autocolorscale` must be true for this attribute to work.';
1417

1518
module.exports = {
1619
editType: 'calc',
17-
sequential: {
18-
valType: 'colorscale',
19-
dflt: scales.Reds,
20-
role: 'style',
21-
editType: 'calc',
22-
description: [
23-
'Sets the default sequential colorscale for positive values.',
24-
msg
25-
].join(' ')
26-
},
27-
sequentialminus: {
28-
valType: 'colorscale',
29-
dflt: scales.Blues,
30-
role: 'style',
20+
21+
colorscale: {
3122
editType: 'calc',
32-
description: [
33-
'Sets the default sequential colorscale for negative values.',
34-
msg
35-
].join(' ')
23+
24+
sequential: {
25+
valType: 'colorscale',
26+
dflt: scales.Reds,
27+
role: 'style',
28+
editType: 'calc',
29+
description: [
30+
'Sets the default sequential colorscale for positive values.',
31+
msg
32+
].join(' ')
33+
},
34+
sequentialminus: {
35+
valType: 'colorscale',
36+
dflt: scales.Blues,
37+
role: 'style',
38+
editType: 'calc',
39+
description: [
40+
'Sets the default sequential colorscale for negative values.',
41+
msg
42+
].join(' ')
43+
},
44+
diverging: {
45+
valType: 'colorscale',
46+
dflt: scales.RdBu,
47+
role: 'style',
48+
editType: 'calc',
49+
description: [
50+
'Sets the default diverging colorscale.',
51+
msg
52+
].join(' ')
53+
}
3654
},
37-
diverging: {
38-
valType: 'colorscale',
39-
dflt: scales.RdBu,
40-
role: 'style',
55+
56+
coloraxis: extendFlat({
57+
// not really a 'subplot' attribute container,
58+
// but this is the flag we use to denote attributes that
59+
// support yaxis, yaxis2, yaxis3, ... counters
60+
_isSubplotObj: true,
4161
editType: 'calc',
4262
description: [
43-
'Sets the default diverging colorscale.',
44-
msg
63+
''
4564
].join(' ')
46-
}
65+
}, colorScaleAttrs('', {
66+
colorAttr: 'corresponding trace color array(s)',
67+
noColorAxis: true,
68+
showScaleDflt: true
69+
}))
4770
};

src/components/colorscale/layout_defaults.js

+31-7
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,41 @@
99
'use strict';
1010

1111
var Lib = require('../../lib');
12-
var colorscaleAttrs = require('./layout_attributes');
1312
var Template = require('../../plot_api/plot_template');
1413

14+
var colorScaleAttrs = require('./layout_attributes');
15+
var colorScaleDefaults = require('./defaults');
16+
1517
module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
16-
var colorscaleIn = layoutIn.colorscale;
17-
var colorscaleOut = Template.newContainer(layoutOut, 'colorscale');
1818
function coerce(attr, dflt) {
19-
return Lib.coerce(colorscaleIn, colorscaleOut, colorscaleAttrs, attr, dflt);
19+
return Lib.coerce(layoutIn, layoutOut, colorScaleAttrs, attr, dflt);
2020
}
2121

22-
coerce('sequential');
23-
coerce('sequentialminus');
24-
coerce('diverging');
22+
coerce('colorscale.sequential');
23+
coerce('colorscale.sequentialminus');
24+
coerce('colorscale.diverging');
25+
26+
var colorAxes = layoutOut._colorAxes;
27+
var colorAxIn, colorAxOut;
28+
29+
function coerceAx(attr, dflt) {
30+
return Lib.coerce(colorAxIn, colorAxOut, colorScaleAttrs.coloraxis, attr, dflt);
31+
}
32+
33+
for(var k in colorAxes) {
34+
var stash = colorAxes[k];
35+
36+
if(stash[0]) {
37+
colorAxIn = layoutIn[k] || {};
38+
colorAxOut = Template.newContainer(layoutOut, k, 'coloraxis');
39+
colorAxOut._name = k;
40+
colorScaleDefaults(colorAxIn, colorAxOut, layoutOut, coerceAx, {prefix: '', cLetter: 'c'});
41+
} else {
42+
// re-coerce colorscale attributes w/o coloraxis
43+
for(var i = 0; i < stash[2].length; i++) {
44+
stash[2][i]();
45+
}
46+
delete layoutOut._colorAxes[k];
47+
}
48+
}
2549
};

src/plot_api/plot_schema.js

+16-14
Original file line numberDiff line numberDiff line change
@@ -544,24 +544,26 @@ function getLayoutAttributes() {
544544
var schema = _module.schema;
545545

546546
if(schema && (schema.subplots || schema.layout)) {
547-
/*
548-
* Components with defined schema have already been merged in at register time
549-
* but a few components define attributes that apply only to xaxis
550-
* not yaxis (rangeselector, rangeslider) - delete from y schema.
551-
* Note that the input attributes for xaxis/yaxis are the same object
552-
* so it's not possible to only add them to xaxis from the start.
553-
* If we ever have such asymmetry the other way, or anywhere else,
554-
* we will need to extend both this code and mergeComponentAttrsToSubplot
555-
* (which will not find yaxis only for example)
556-
*/
557-
547+
/*
548+
* Components with defined schema have already been merged in at register time
549+
* but a few components define attributes that apply only to xaxis
550+
* not yaxis (rangeselector, rangeslider) - delete from y schema.
551+
* Note that the input attributes for xaxis/yaxis are the same object
552+
* so it's not possible to only add them to xaxis from the start.
553+
* If we ever have such asymmetry the other way, or anywhere else,
554+
* we will need to extend both this code and mergeComponentAttrsToSubplot
555+
* (which will not find yaxis only for example)
556+
*/
558557
var subplots = schema.subplots;
559558
if(subplots && subplots.xaxis && !subplots.yaxis) {
560-
for(var xkey in subplots.xaxis) delete layoutAttributes.yaxis[xkey];
559+
for(var xkey in subplots.xaxis) {
560+
delete layoutAttributes.yaxis[xkey];
561+
}
561562
}
563+
} else if(_module.name === 'colorscale') {
564+
extendDeepAll(layoutAttributes, _module.layoutAttributes);
562565
} else if(_module.layoutAttributes) {
563-
// older style without schema need to be explicitly merged in now
564-
566+
// older style without schema need to be explicitly merged in now
565567
insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name);
566568
}
567569
}

src/plots/layout_attributes.js

-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
var fontAttrs = require('./font_attributes');
1212
var animationAttrs = require('./animation_attributes');
1313
var colorAttrs = require('../components/color/attributes');
14-
var colorscaleAttrs = require('../components/colorscale/layout_attributes');
1514
var padAttrs = require('./pad_attributes');
1615
var extendFlat = require('../lib/extend').extendFlat;
1716

@@ -292,7 +291,6 @@ module.exports = {
292291
editType: 'calc',
293292
description: 'Sets the default trace colors.'
294293
},
295-
colorscale: colorscaleAttrs,
296294
datarevision: {
297295
valType: 'any',
298296
role: 'info',

src/plots/plots.js

+2
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,8 @@ plots.supplyDefaults = function(gd, opts) {
390390
newFullLayout._firstScatter = {};
391391
// for grouped bar/box/violin trace to share config across traces
392392
newFullLayout._alignmentOpts = {};
393+
// track color axes referenced in the data
394+
newFullLayout._colorAxes = {};
393395

394396
// for traces to request a default rangeslider on their x axes
395397
// eg set `_requestRangeslider.x2 = true` for xaxis2

test/jasmine/bundle_tests/plotschema_test.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ describe('plot schema', function() {
136136
'xaxis', 'yaxis', 'scene', 'geo', 'ternary', 'mapbox', 'polar',
137137
// not really a 'subplot' object but supports yaxis, yaxis2, yaxis3,
138138
// ... counters, so list it here
139-
'xaxis.rangeslider.yaxis'
139+
'xaxis.rangeslider.yaxis',
140+
'coloraxis'
140141
];
141142

142143
// check if the subplot objects have '_isSubplotObj'
@@ -146,7 +147,7 @@ describe('plot schema', function() {
146147
plotSchema.layout.layoutAttributes,
147148
astr + '.' + IS_SUBPLOT_OBJ
148149
).get()
149-
).toBe(true);
150+
).toBe(true, astr);
150151
});
151152

152153
// check that no other object has '_isSubplotObj'

0 commit comments

Comments
 (0)