Skip to content

Commit d5ebb50

Browse files
authored
Merge pull request #3130 from plotly/2951-contrasting-pie-and-bar-inside-text
2951 contrasting pie and bar inside text
2 parents 36d9fb4 + 69b35b5 commit d5ebb50

27 files changed

+799
-147
lines changed

Diff for: src/traces/bar/defaults.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,21 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
4646

4747
if(hasInside || hasOutside) {
4848
var textFont = coerceFont(coerce, 'textfont', layout.font);
49-
if(hasInside) coerceFont(coerce, 'insidetextfont', textFont);
49+
50+
// Note that coercing `insidetextfont` is always needed –
51+
// even if `textposition` is `outside` for each trace – since
52+
// an outside label can become an inside one, for example because
53+
// of a bar being stacked on top of it.
54+
var insideTextFontDefault = Lib.extendFlat({}, textFont);
55+
var isTraceTextfontColorSet = traceIn.textfont && traceIn.textfont.color;
56+
var isColorInheritedFromLayoutFont = !isTraceTextfontColorSet;
57+
if(isColorInheritedFromLayoutFont) {
58+
delete insideTextFontDefault.color;
59+
}
60+
coerceFont(coerce, 'insidetextfont', insideTextFontDefault);
61+
5062
if(hasOutside) coerceFont(coerce, 'outsidetextfont', textFont);
63+
5164
coerce('constraintext');
5265
coerce('selected.textfont.color');
5366
coerce('unselected.textfont.color');

Diff for: src/traces/bar/helpers.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Copyright 2012-2018, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var isNumeric = require('fast-isnumeric');
12+
var tinycolor = require('tinycolor2');
13+
14+
exports.coerceString = function(attributeDefinition, value, defaultValue) {
15+
if(typeof value === 'string') {
16+
if(value || !attributeDefinition.noBlank) return value;
17+
}
18+
else if(typeof value === 'number') {
19+
if(!attributeDefinition.strict) return String(value);
20+
}
21+
22+
return (defaultValue !== undefined) ?
23+
defaultValue :
24+
attributeDefinition.dflt;
25+
};
26+
27+
exports.coerceNumber = function(attributeDefinition, value, defaultValue) {
28+
if(isNumeric(value)) {
29+
value = +value;
30+
31+
var min = attributeDefinition.min,
32+
max = attributeDefinition.max,
33+
isOutOfBounds = (min !== undefined && value < min) ||
34+
(max !== undefined && value > max);
35+
36+
if(!isOutOfBounds) return value;
37+
}
38+
39+
return (defaultValue !== undefined) ?
40+
defaultValue :
41+
attributeDefinition.dflt;
42+
};
43+
44+
exports.coerceColor = function(attributeDefinition, value, defaultValue) {
45+
if(tinycolor(value).isValid()) return value;
46+
47+
return (defaultValue !== undefined) ?
48+
defaultValue :
49+
attributeDefinition.dflt;
50+
};
51+
52+
exports.coerceEnumerated = function(attributeDefinition, value, defaultValue) {
53+
if(attributeDefinition.coerceNumber) value = +value;
54+
55+
if(attributeDefinition.values.indexOf(value) !== -1) return value;
56+
57+
return (defaultValue !== undefined) ?
58+
defaultValue :
59+
attributeDefinition.dflt;
60+
};
61+
62+
exports.getValue = function(arrayOrScalar, index) {
63+
var value;
64+
if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar;
65+
else if(index < arrayOrScalar.length) value = arrayOrScalar[index];
66+
return value;
67+
};

Diff for: src/traces/bar/plot.js

+11-99
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
var d3 = require('d3');
1313
var isNumeric = require('fast-isnumeric');
14-
var tinycolor = require('tinycolor2');
1514

1615
var Lib = require('../../lib');
1716
var svgTextUtils = require('../../lib/svg_text_utils');
@@ -22,10 +21,9 @@ var Registry = require('../../registry');
2221

2322
var attributes = require('./attributes'),
2423
attributeText = attributes.text,
25-
attributeTextPosition = attributes.textposition,
26-
attributeTextFont = attributes.textfont,
27-
attributeInsideTextFont = attributes.insidetextfont,
28-
attributeOutsideTextFont = attributes.outsidetextfont;
24+
attributeTextPosition = attributes.textposition;
25+
var helpers = require('./helpers');
26+
var style = require('./style');
2927

3028
// padding in pixels around text
3129
var TEXTPAD = 3;
@@ -177,9 +175,10 @@ function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
177175
return;
178176
}
179177

180-
var textFont = getTextFont(trace, i, gd._fullLayout.font),
181-
insideTextFont = getInsideTextFont(trace, i, textFont),
182-
outsideTextFont = getOutsideTextFont(trace, i, textFont);
178+
var layoutFont = gd._fullLayout.font;
179+
var barColor = style.getBarColor(calcTrace[i], trace);
180+
var insideTextFont = style.getInsideTextFont(trace, i, layoutFont, barColor);
181+
var outsideTextFont = style.getOutsideTextFont(trace, i, layoutFont);
183182

184183
// compute text position
185184
var barmode = gd._fullLayout.barmode,
@@ -429,98 +428,11 @@ function getTransform(textX, textY, targetX, targetY, scale, rotate) {
429428
}
430429

431430
function getText(trace, index) {
432-
var value = getValue(trace.text, index);
433-
return coerceString(attributeText, value);
431+
var value = helpers.getValue(trace.text, index);
432+
return helpers.coerceString(attributeText, value);
434433
}
435434

436435
function getTextPosition(trace, index) {
437-
var value = getValue(trace.textposition, index);
438-
return coerceEnumerated(attributeTextPosition, value);
439-
}
440-
441-
function getTextFont(trace, index, defaultValue) {
442-
return getFontValue(
443-
attributeTextFont, trace.textfont, index, defaultValue);
444-
}
445-
446-
function getInsideTextFont(trace, index, defaultValue) {
447-
return getFontValue(
448-
attributeInsideTextFont, trace.insidetextfont, index, defaultValue);
449-
}
450-
451-
function getOutsideTextFont(trace, index, defaultValue) {
452-
return getFontValue(
453-
attributeOutsideTextFont, trace.outsidetextfont, index, defaultValue);
454-
}
455-
456-
function getFontValue(attributeDefinition, attributeValue, index, defaultValue) {
457-
attributeValue = attributeValue || {};
458-
459-
var familyValue = getValue(attributeValue.family, index),
460-
sizeValue = getValue(attributeValue.size, index),
461-
colorValue = getValue(attributeValue.color, index);
462-
463-
return {
464-
family: coerceString(
465-
attributeDefinition.family, familyValue, defaultValue.family),
466-
size: coerceNumber(
467-
attributeDefinition.size, sizeValue, defaultValue.size),
468-
color: coerceColor(
469-
attributeDefinition.color, colorValue, defaultValue.color)
470-
};
471-
}
472-
473-
function getValue(arrayOrScalar, index) {
474-
var value;
475-
if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar;
476-
else if(index < arrayOrScalar.length) value = arrayOrScalar[index];
477-
return value;
478-
}
479-
480-
function coerceString(attributeDefinition, value, defaultValue) {
481-
if(typeof value === 'string') {
482-
if(value || !attributeDefinition.noBlank) return value;
483-
}
484-
else if(typeof value === 'number') {
485-
if(!attributeDefinition.strict) return String(value);
486-
}
487-
488-
return (defaultValue !== undefined) ?
489-
defaultValue :
490-
attributeDefinition.dflt;
491-
}
492-
493-
function coerceEnumerated(attributeDefinition, value, defaultValue) {
494-
if(attributeDefinition.coerceNumber) value = +value;
495-
496-
if(attributeDefinition.values.indexOf(value) !== -1) return value;
497-
498-
return (defaultValue !== undefined) ?
499-
defaultValue :
500-
attributeDefinition.dflt;
501-
}
502-
503-
function coerceNumber(attributeDefinition, value, defaultValue) {
504-
if(isNumeric(value)) {
505-
value = +value;
506-
507-
var min = attributeDefinition.min,
508-
max = attributeDefinition.max,
509-
isOutOfBounds = (min !== undefined && value < min) ||
510-
(max !== undefined && value > max);
511-
512-
if(!isOutOfBounds) return value;
513-
}
514-
515-
return (defaultValue !== undefined) ?
516-
defaultValue :
517-
attributeDefinition.dflt;
518-
}
519-
520-
function coerceColor(attributeDefinition, value, defaultValue) {
521-
if(tinycolor(value).isValid()) return value;
522-
523-
return (defaultValue !== undefined) ?
524-
defaultValue :
525-
attributeDefinition.dflt;
436+
var value = helpers.getValue(trace.textposition, index);
437+
return helpers.coerceEnumerated(attributeTextPosition, value);
526438
}

Diff for: src/traces/bar/style.js

+104-18
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@
1010
'use strict';
1111

1212
var d3 = require('d3');
13+
var Color = require('../../components/color');
1314
var Drawing = require('../../components/drawing');
15+
var Lib = require('../../lib');
1416
var Registry = require('../../registry');
1517

18+
var attributes = require('./attributes'),
19+
attributeTextFont = attributes.textfont,
20+
attributeInsideTextFont = attributes.insidetextfont,
21+
attributeOutsideTextFont = attributes.outsidetextfont;
22+
var helpers = require('./helpers');
23+
1624
function style(gd, cd) {
1725
var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.bars');
1826
var barcount = s.size();
@@ -50,21 +58,8 @@ function stylePoints(sel, trace, gd) {
5058

5159
txs.each(function(d) {
5260
var tx = d3.select(this);
53-
var textFont;
54-
55-
if(tx.classed('bartext-inside')) {
56-
textFont = trace.insidetextfont;
57-
} else if(tx.classed('bartext-outside')) {
58-
textFont = trace.outsidetextfont;
59-
}
60-
if(!textFont) textFont = trace.textfont;
61-
62-
function cast(k) {
63-
var cont = textFont[k];
64-
return Array.isArray(cont) ? cont[d.i] : cont;
65-
}
66-
67-
Drawing.font(tx, cast('family'), cast('size'), cast('color'));
61+
var font = determineFont(tx, d, trace, gd);
62+
Drawing.font(tx, font);
6863
});
6964
}
7065

@@ -73,14 +68,105 @@ function styleOnSelect(gd, cd) {
7368
var trace = cd[0].trace;
7469

7570
if(trace.selectedpoints) {
76-
Drawing.selectedPointStyle(s.selectAll('path'), trace);
77-
Drawing.selectedTextStyle(s.selectAll('text'), trace);
71+
stylePointsInSelectionMode(s, trace, gd);
7872
} else {
7973
stylePoints(s, trace, gd);
8074
}
8175
}
8276

77+
function stylePointsInSelectionMode(s, trace, gd) {
78+
Drawing.selectedPointStyle(s.selectAll('path'), trace);
79+
styleTextInSelectionMode(s.selectAll('text'), trace, gd);
80+
}
81+
82+
function styleTextInSelectionMode(txs, trace, gd) {
83+
txs.each(function(d) {
84+
var tx = d3.select(this);
85+
var font;
86+
87+
if(d.selected) {
88+
font = Lib.extendFlat({}, determineFont(tx, d, trace, gd));
89+
90+
var selectedFontColor = trace.selected.textfont && trace.selected.textfont.color;
91+
if(selectedFontColor) {
92+
font.color = selectedFontColor;
93+
}
94+
95+
Drawing.font(tx, font);
96+
} else {
97+
Drawing.selectedTextStyle(tx, trace);
98+
}
99+
});
100+
}
101+
102+
function determineFont(tx, d, trace, gd) {
103+
var layoutFont = gd._fullLayout.font;
104+
var textFont = trace.textfont;
105+
106+
if(tx.classed('bartext-inside')) {
107+
var barColor = getBarColor(d, trace);
108+
textFont = getInsideTextFont(trace, d.i, layoutFont, barColor);
109+
} else if(tx.classed('bartext-outside')) {
110+
textFont = getOutsideTextFont(trace, d.i, layoutFont);
111+
}
112+
113+
return textFont;
114+
}
115+
116+
function getTextFont(trace, index, defaultValue) {
117+
return getFontValue(
118+
attributeTextFont, trace.textfont, index, defaultValue);
119+
}
120+
121+
function getInsideTextFont(trace, index, layoutFont, barColor) {
122+
var defaultFont = getTextFont(trace, index, layoutFont);
123+
124+
var wouldFallBackToLayoutFont =
125+
(trace._input.textfont === undefined || trace._input.textfont.color === undefined) ||
126+
(Array.isArray(trace.textfont.color) && trace.textfont.color[index] === undefined);
127+
if(wouldFallBackToLayoutFont) {
128+
defaultFont = {
129+
color: Color.contrast(barColor),
130+
family: defaultFont.family,
131+
size: defaultFont.size
132+
};
133+
}
134+
135+
return getFontValue(
136+
attributeInsideTextFont, trace.insidetextfont, index, defaultFont);
137+
}
138+
139+
function getOutsideTextFont(trace, index, layoutFont) {
140+
var defaultFont = getTextFont(trace, index, layoutFont);
141+
return getFontValue(
142+
attributeOutsideTextFont, trace.outsidetextfont, index, defaultFont);
143+
}
144+
145+
function getFontValue(attributeDefinition, attributeValue, index, defaultValue) {
146+
attributeValue = attributeValue || {};
147+
148+
var familyValue = helpers.getValue(attributeValue.family, index),
149+
sizeValue = helpers.getValue(attributeValue.size, index),
150+
colorValue = helpers.getValue(attributeValue.color, index);
151+
152+
return {
153+
family: helpers.coerceString(
154+
attributeDefinition.family, familyValue, defaultValue.family),
155+
size: helpers.coerceNumber(
156+
attributeDefinition.size, sizeValue, defaultValue.size),
157+
color: helpers.coerceColor(
158+
attributeDefinition.color, colorValue, defaultValue.color)
159+
};
160+
}
161+
162+
function getBarColor(cd, trace) {
163+
return cd.mc || trace.marker.color;
164+
}
165+
83166
module.exports = {
84167
style: style,
85-
styleOnSelect: styleOnSelect
168+
styleOnSelect: styleOnSelect,
169+
getInsideTextFont: getInsideTextFont,
170+
getOutsideTextFont: getOutsideTextFont,
171+
getBarColor: getBarColor
86172
};

0 commit comments

Comments
 (0)