Skip to content

Commit ace820b

Browse files
committed
Update shape defaults to handle an array of references
1 parent a7b3bb2 commit ace820b

2 files changed

Lines changed: 67 additions & 6 deletions

File tree

src/components/shapes/defaults.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,39 @@ function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
6666

6767
// positioning
6868
var axLetters = ['x', 'y'];
69-
for(var i = 0; i < 2; i++) {
70-
var axLetter = axLetters[i];
69+
axLetters.forEach(function(axLetter) {
7170
var attrAnchor = axLetter + 'anchor';
7271
var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode;
7372
var gdMock = {_fullLayout: fullLayout};
7473
var ax;
7574
var pos2r;
7675
var r2pos;
7776

78-
// xref, yref
79-
var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined,
80-
'paper');
77+
// xref, yref - handle both string and array values
78+
var axRef;
79+
var refAttr = axLetter + 'ref';
80+
var inputRef = shapeIn[refAttr];
81+
82+
if(Array.isArray(inputRef) && inputRef.length > 0) {
83+
// Array case: use coerceRefArray for validation
84+
var expectedLen = helpers.countDefiningCoords(path, noPath);
85+
axRef = Axes.coerceRefArray(shapeIn, shapeOut, gdMock, axLetter, expectedLen);
86+
shapeOut['_' + axLetter + 'refArray'] = true;
87+
88+
// Need to register the shape with all referenced axes for redrawing purposes
89+
axRef.forEach(function(ref) {
90+
if(Axes.getRefType(ref) === 'range') {
91+
ax = Axes.getFromId(gdMock, ref);
92+
if(ax && ax._shapeIndices.indexOf(shapeOut._index) === -1) {
93+
ax._shapeIndices.push(shapeOut._index);
94+
}
95+
}
96+
});
97+
} else {
98+
// String/undefined case: use coerceRef
99+
axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined, 'paper');
100+
}
101+
81102
var axRefType = Axes.getRefType(axRef);
82103

83104
if(axRefType === 'range') {
@@ -136,7 +157,7 @@ function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
136157
shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
137158
shapeIn[attrAnchor] = inAnchor;
138159
}
139-
}
160+
});
140161

141162
if(noPath) {
142163
Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']);

src/plots/cartesian/axes.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ var Drawing = require('../../components/drawing');
1414

1515
var axAttrs = require('./layout_attributes');
1616
var cleanTicks = require('./clean_ticks');
17+
var cartesianConstants = require('./constants');
1718

1819
var constants = require('../../constants/numerical');
1920
var ONEMAXYEAR = constants.ONEMAXYEAR;
@@ -124,6 +125,44 @@ axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption
124125
return Lib.coerce(containerIn, containerOut, attrDef, refAttr);
125126
};
126127

128+
/*
129+
* Coerce an array of axis references. Used by shapes for per-coordinate axis references.
130+
*
131+
* attr: the attribute we're generating a reference for. Should end in 'x' or 'y'
132+
* but can be prefixed, like 'ax' for annotation's arrow x
133+
* dflt: the default to coerce to, or blank to use the first axis (falling back on
134+
* extraOption if there is no axis)
135+
* extraOption: aside from existing axes with this letter, what non-axis value is allowed?
136+
* Only required if it's different from `dflt`
137+
*/
138+
axes.coerceRefArray = function(containerIn, containerOut, gd, attr, expectedLen) {
139+
var axLetter = attr.charAt(attr.length - 1);
140+
var axlist = gd._fullLayout._subplots[axLetter + 'axis'];
141+
axlist = axlist.concat(axlist.map(function(x) { return x + ' domain'; }));
142+
var refAttr = attr + 'ref';
143+
var axRef = containerIn[refAttr];
144+
var dflt = axlist.length ? axlist[0] : 'paper';
145+
146+
// Handle array length mismatch
147+
if(axRef.length > expectedLen) {
148+
// if the array is longer than the expected length, truncate it
149+
axRef = axRef.slice(0, expectedLen);
150+
} else if(axRef.length < expectedLen) {
151+
// if the array is shorter than the expected length, extend using the default value
152+
axRef = axRef.concat(Array(expectedLen - axRef.length).fill(dflt));
153+
}
154+
155+
// Check all references, replace with default if invalid
156+
for(var i = 0; i < axRef.length; i++) {
157+
if(!(axRef[i] === 'paper' || cartesianConstants.idRegex[axLetter].test(axRef[i]))) {
158+
axRef[i] = dflt;
159+
}
160+
}
161+
162+
containerOut[refAttr] = axRef;
163+
return axRef;
164+
};
165+
127166
/*
128167
* Get the type of an axis reference. This can be 'range', 'domain', or 'paper'.
129168
* This assumes ar is a valid axis reference and returns 'range' if it doesn't
@@ -134,6 +173,7 @@ axes.coerceRef = function(containerIn, containerOut, gd, attr, dflt, extraOption
134173
*/
135174
axes.getRefType = function(ar) {
136175
if(ar === undefined) { return ar; }
176+
if(Array.isArray(ar)) { return 'array'; }
137177
if(ar === 'paper') { return 'paper'; }
138178
if(ar === 'pixel') { return 'pixel'; }
139179
if(/( domain)$/.test(ar)) { return 'domain'; } else { return 'range'; }

0 commit comments

Comments
 (0)