@@ -68,77 +68,138 @@ function handleShapeDefaults(shapeIn, shapeOut, fullLayout) {
6868 var ySizeMode = coerce('ysizemode');
6969
7070 // positioning
71- var axLetters = ['x', 'y'];
72- for (var i = 0; i < 2; i++) {
73- var axLetter = axLetters[i];
71+ const dflts = [0.25, 0.75];
72+ const pixelDflts = [0, 10];
73+
74+ ['x', 'y'].forEach(axLetter => {
7475 var attrAnchor = axLetter + 'anchor';
7576 var sizeMode = axLetter === 'x' ? xSizeMode : ySizeMode;
7677 var gdMock = { _fullLayout: fullLayout };
7778 var ax;
7879 var pos2r;
7980 var r2pos;
8081
81- // xref, yref
82- var axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined, 'paper');
83- var axRefType = Axes.getRefType(axRef);
84-
85- if (axRefType === 'range') {
86- ax = Axes.getFromId(gdMock, axRef);
87- ax._shapeIndices.push(shapeOut._index);
88- r2pos = helpers.rangeToShapePosition(ax);
89- pos2r = helpers.shapePositionToRange(ax);
90- if (ax.type === 'category' || ax.type === 'multicategory') {
91- coerce(axLetter + '0shift');
92- coerce(axLetter + '1shift');
93- }
82+ // xref, yref - handle both string and array values
83+ var axRef;
84+ const refAttr = axLetter + 'ref';
85+ const inputRef = shapeIn[refAttr];
86+
87+ if(Array.isArray(inputRef) && inputRef.length > 0) {
88+ // Array case: use coerceRefArray for validation
89+ const expectedLen = helpers.countDefiningCoords(shapeType, path, axLetter);
90+ axRef = Axes.coerceRefArray(shapeIn, shapeOut, gdMock, axLetter, undefined, 'paper', expectedLen);
91+ shapeOut['_' + axLetter + 'refArray'] = true;
9492 } else {
95- pos2r = r2pos = Lib.identity;
93+ // String/undefined case: use coerceRef
94+ axRef = Axes.coerceRef(shapeIn, shapeOut, gdMock, axLetter, undefined, 'paper');
9695 }
9796
98- // Coerce x0, x1, y0, y1
99- if (noPath) {
100- var dflt0 = 0.25;
101- var dflt1 = 0.75;
102-
103- // hack until V3.0 when log has regular range behavior - make it look like other
104- // ranges to send to coerce, then put it back after
105- // this is all to give reasonable default position behavior on log axes, which is
106- // a pretty unimportant edge case so we could just ignore this.
107- var attr0 = axLetter + '0';
108- var attr1 = axLetter + '1';
109- var in0 = shapeIn[attr0];
110- var in1 = shapeIn[attr1];
111- shapeIn[attr0] = pos2r(shapeIn[attr0], true);
112- shapeIn[attr1] = pos2r(shapeIn[attr1], true);
113-
114- if (sizeMode === 'pixel') {
115- coerce(attr0, 0);
116- coerce(attr1, 10);
97+ if(Array.isArray(axRef)) {
98+ // Register the shape with all referenced axes for redrawing purposes
99+ axRef.forEach(function(ref) {
100+ if(Axes.getRefType(ref) === 'range') {
101+ ax = Axes.getFromId(gdMock, ref);
102+ if(ax && ax._shapeIndices.indexOf(shapeOut._index) === -1) {
103+ ax._shapeIndices.push(shapeOut._index);
104+ }
105+ }
106+ });
107+
108+ if(noPath) {
109+ [0, 1].forEach(function(i) {
110+ const ref = axRef[i];
111+ const refType = Axes.getRefType(ref);
112+ if(refType === 'range') {
113+ ax = Axes.getFromId(gdMock, ref);
114+ pos2r = helpers.shapePositionToRange(ax);
115+ r2pos = helpers.rangeToShapePosition(ax);
116+ if(ax.type === 'category' || ax.type === 'multicategory') {
117+ coerce(axLetter + i + 'shift');
118+ }
119+ } else {
120+ pos2r = r2pos = Lib.identity;
121+ }
122+
123+ const attr = axLetter + i;
124+ const inValue = shapeIn[attr];
125+ shapeIn[attr] = pos2r(shapeIn[attr], true);
126+
127+ if(sizeMode === 'pixel') {
128+ coerce(attr, pixelDflts[i]);
129+ } else {
130+ Axes.coercePosition(shapeOut, gdMock, coerce, ref, attr, dflts[i]);
131+ }
132+
133+ shapeOut[attr] = r2pos(shapeOut[attr]);
134+ shapeIn[attr] = inValue;
135+
136+ if(i === 0 && sizeMode === 'pixel') {
137+ const inAnchor = shapeIn[attrAnchor];
138+ shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
139+ Axes.coercePosition(shapeOut, gdMock, coerce, ref, attrAnchor, 0.25);
140+ shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
141+ shapeIn[attrAnchor] = inAnchor;
142+ }
143+ });
144+ }
145+ } else {
146+ const axRefType = Axes.getRefType(axRef);
147+
148+ if(axRefType === 'range') {
149+ ax = Axes.getFromId(gdMock, axRef);
150+ ax._shapeIndices.push(shapeOut._index);
151+ r2pos = helpers.rangeToShapePosition(ax);
152+ pos2r = helpers.shapePositionToRange(ax);
153+ if(noPath && (ax.type === 'category' || ax.type === 'multicategory')) {
154+ coerce(axLetter + '0shift');
155+ coerce(axLetter + '1shift');
156+ }
117157 } else {
118- Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflt0);
119- Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflt1);
158+ pos2r = r2pos = Lib.identity;
120159 }
121160
122- // hack part 2
123- shapeOut[attr0] = r2pos(shapeOut[attr0]);
124- shapeOut[attr1] = r2pos(shapeOut[attr1]);
125- shapeIn[attr0] = in0;
126- shapeIn[attr1] = in1;
127- }
161+ // Coerce x0, x1, y0, y1
162+ if(noPath) {
163+ // hack until V3.0 when log has regular range behavior - make it look like other
164+ // ranges to send to coerce, then put it back after
165+ // this is all to give reasonable default position behavior on log axes, which is
166+ // a pretty unimportant edge case so we could just ignore this.
167+ const attr0 = axLetter + '0';
168+ const attr1 = axLetter + '1';
169+ const in0 = shapeIn[attr0];
170+ const in1 = shapeIn[attr1];
171+ shapeIn[attr0] = pos2r(shapeIn[attr0], true);
172+ shapeIn[attr1] = pos2r(shapeIn[attr1], true);
173+
174+ if(sizeMode === 'pixel') {
175+ coerce(attr0, pixelDflts[0]);
176+ coerce(attr1, pixelDflts[1]);
177+ } else {
178+ Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr0, dflts[0]);
179+ Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attr1, dflts[1]);
180+ }
181+
182+ // hack part 2
183+ shapeOut[attr0] = r2pos(shapeOut[attr0]);
184+ shapeOut[attr1] = r2pos(shapeOut[attr1]);
185+ shapeIn[attr0] = in0;
186+ shapeIn[attr1] = in1;
187+ }
128188
129- // Coerce xanchor and yanchor
130- if (sizeMode === 'pixel') {
131- // Hack for log axis described above
132- var inAnchor = shapeIn[attrAnchor];
133- shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
189+ // Coerce xanchor and yanchor
190+ if (sizeMode === 'pixel') {
191+ // Hack for log axis described above
192+ const inAnchor = shapeIn[attrAnchor];
193+ shapeIn[attrAnchor] = pos2r(shapeIn[attrAnchor], true);
134194
135- Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);
195+ Axes.coercePosition(shapeOut, gdMock, coerce, axRef, attrAnchor, 0.25);
136196
137- // Hack part 2
138- shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
139- shapeIn[attrAnchor] = inAnchor;
197+ // Hack part 2
198+ shapeOut[attrAnchor] = r2pos(shapeOut[attrAnchor]);
199+ shapeIn[attrAnchor] = inAnchor;
200+ }
140201 }
141- }
202+ });
142203
143204 if (noPath) {
144205 Lib.noneOrAll(shapeIn, shapeOut, ['x0', 'x1', 'y0', 'y1']);
0 commit comments