Skip to content

Commit 5ce2a83

Browse files
authored
Merge pull request #3876 from plotly/funnelarea-traces
Funnelarea traces
2 parents 1ce7848 + b8b3d94 commit 5ce2a83

File tree

80 files changed

+4303
-281
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+4303
-281
lines changed

Diff for: lib/funnelarea.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2019, 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+
module.exports = require('../src/traces/funnelarea');

Diff for: lib/index-finance.js

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Plotly.register([
1414
require('./bar'),
1515
require('./histogram'),
1616
require('./pie'),
17+
require('./funnelarea'),
1718
require('./ohlc'),
1819
require('./candlestick'),
1920
require('./funnel'),

Diff for: lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Plotly.register([
2626

2727
require('./pie'),
2828
require('./sunburst'),
29+
require('./funnelarea'),
2930

3031
require('./scatter3d'),
3132
require('./surface'),

Diff for: src/components/fx/calc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module.exports = function calc(gd) {
2828
// don't include hover calc fields for pie traces
2929
// as calcdata items might be sorted by value and
3030
// won't match the data array order.
31-
if(Registry.traceIs(trace, 'pie')) continue;
31+
if(Registry.traceIs(trace, 'pie-like')) continue;
3232

3333
var fillFn = Registry.traceIs(trace, '2dMap') ? paste : Lib.fillArray;
3434

Diff for: src/components/legend/defaults.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var Registry = require('../../registry');
@@ -42,7 +41,7 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) {
4241
legendReallyHasATrace = true;
4342
// Always show the legend by default if there's a pie,
4443
// or if there's only one trace but it's explicitly shown
45-
if(Registry.traceIs(trace, 'pie') ||
44+
if(Registry.traceIs(trace, 'pie-like') ||
4645
trace._input.showlegend === true
4746
) {
4847
legendTraceCount++;

Diff for: src/components/legend/draw.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ module.exports = function draw(gd) {
5858
for(var j = 0; j < legendData[i].length; j++) {
5959
var item = legendData[i][j][0];
6060
var trace = item.trace;
61-
var isPie = Registry.traceIs(trace, 'pie');
62-
var name = isPie ? item.label : trace.name;
61+
var isPieLike = Registry.traceIs(trace, 'pie-like');
62+
var name = isPieLike ? item.label : trace.name;
6363
maxLength = Math.max(maxLength, name && name.length || 0);
6464
}
6565
}
@@ -110,7 +110,7 @@ module.exports = function draw(gd) {
110110

111111
traces.style('opacity', function(d) {
112112
var trace = d[0].trace;
113-
if(Registry.traceIs(trace, 'pie')) {
113+
if(Registry.traceIs(trace, 'pie-like')) {
114114
return hiddenSlices.indexOf(d[0].label) !== -1 ? 0.5 : 1;
115115
} else {
116116
return trace.visible === 'legendonly' ? 0.5 : 1;
@@ -375,7 +375,7 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
375375
if(trace._group) {
376376
evtData.group = trace._group;
377377
}
378-
if(trace.type === 'pie') {
378+
if(Registry.traceIs(trace, 'pie-like')) {
379379
evtData.label = legendItem.datum()[0].label;
380380
}
381381

@@ -399,11 +399,11 @@ function drawTexts(g, gd, maxLength) {
399399
var legendItem = g.data()[0][0];
400400
var fullLayout = gd._fullLayout;
401401
var trace = legendItem.trace;
402-
var isPie = Registry.traceIs(trace, 'pie');
402+
var isPieLike = Registry.traceIs(trace, 'pie-like');
403403
var traceIndex = trace.index;
404-
var isEditable = gd._context.edits.legendText && !isPie;
404+
var isEditable = gd._context.edits.legendText && !isPieLike;
405405

406-
var name = isPie ? legendItem.label : trace.name;
406+
var name = isPieLike ? legendItem.label : trace.name;
407407
if(trace._meta) {
408408
name = Lib.templateString(name, trace._meta);
409409
}

Diff for: src/components/legend/get_legend_data.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var Registry = require('../../registry');
1312
var helpers = require('./helpers');
1413

15-
1614
module.exports = function getLegendData(calcdata, opts) {
1715
var lgroupToTraces = {};
1816
var lgroups = [];
@@ -45,7 +43,7 @@ module.exports = function getLegendData(calcdata, opts) {
4543

4644
if(!trace.visible || !trace.showlegend) continue;
4745

48-
if(Registry.traceIs(trace, 'pie')) {
46+
if(Registry.traceIs(trace, 'pie-like')) {
4947
if(!slicesShown[lgroup]) slicesShown[lgroup] = {};
5048

5149
for(j = 0; j < cd.length; j++) {

Diff for: src/components/legend/handle_click.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ module.exports = function handleClick(g, gd, numClicks) {
104104
}
105105
}
106106

107-
if(Registry.traceIs(fullTrace, 'pie')) {
107+
if(Registry.traceIs(fullTrace, 'pie-like')) {
108108
var thisLabel = legendItem.label;
109109
var thisLabelIndex = hiddenSlices.indexOf(thisLabel);
110110

Diff for: src/components/legend/style.js

+19-7
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ module.exports = function style(s, gd) {
8787
.each(styleFunnels)
8888
.each(styleBars)
8989
.each(styleBoxes)
90+
.each(styleFunnelareas)
9091
.each(stylePies)
9192
.each(styleLines)
9293
.each(stylePoints)
@@ -307,14 +308,14 @@ module.exports = function style(s, gd) {
307308
}
308309

309310
function styleBars(d) {
310-
styleBarFamily(d, this);
311+
styleBarLike(d, this);
311312
}
312313

313314
function styleFunnels(d) {
314-
styleBarFamily(d, this, 'funnel');
315+
styleBarLike(d, this, 'funnel');
315316
}
316317

317-
function styleBarFamily(d, lThis, desiredType) {
318+
function styleBarLike(d, lThis, desiredType) {
318319
var trace = d[0].trace;
319320
var marker = trace.marker || {};
320321
var markerLine = marker.line || {};
@@ -435,13 +436,24 @@ module.exports = function style(s, gd) {
435436
}
436437

437438
function stylePies(d) {
439+
stylePieLike(d, this, 'pie');
440+
}
441+
442+
function styleFunnelareas(d) {
443+
stylePieLike(d, this, 'funnelarea');
444+
}
445+
446+
function stylePieLike(d, lThis, desiredType) {
438447
var d0 = d[0];
439448
var trace = d0.trace;
440449

441-
var pts = d3.select(this).select('g.legendpoints')
442-
.selectAll('path.legendpie')
443-
.data(Registry.traceIs(trace, 'pie') && trace.visible ? [d] : []);
444-
pts.enter().append('path').classed('legendpie', true)
450+
var isVisible = (!desiredType) ? Registry.traceIs(trace, desiredType) :
451+
(trace.type === desiredType && trace.visible);
452+
453+
var pts = d3.select(lThis).select('g.legendpoints')
454+
.selectAll('path.legend' + desiredType)
455+
.data(isVisible ? [d] : []);
456+
pts.enter().append('path').classed('legend' + desiredType, true)
445457
.attr('d', 'M6,6H-6V-6H6Z')
446458
.attr('transform', 'translate(20,0)');
447459
pts.exit().remove();

Diff for: src/components/modebar/manage.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd, showSendToCloud) {
8080
var hasGL3D = fullLayout._has('gl3d');
8181
var hasGeo = fullLayout._has('geo');
8282
var hasPie = fullLayout._has('pie');
83+
var hasFunnelarea = fullLayout._has('funnelarea');
8384
var hasGL2D = fullLayout._has('gl2d');
8485
var hasTernary = fullLayout._has('ternary');
8586
var hasMapbox = fullLayout._has('mapbox');
@@ -113,7 +114,7 @@ function getButtonGroups(gd, buttonsToRemove, buttonsToAdd, showSendToCloud) {
113114
var resetGroup = [];
114115
var dragModeGroup = [];
115116

116-
if((hasCartesian || hasGL2D || hasPie || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) {
117+
if((hasCartesian || hasGL2D || hasPie || hasFunnelarea || hasTernary) + hasGeo + hasGL3D + hasMapbox + hasPolar > 1) {
117118
// graphs with more than one plot types get 'union buttons'
118119
// which reset the view or toggle hover labels across all subplots.
119120
hoverGroup = ['toggleHover'];

Diff for: src/lib/index.js

+12
Original file line numberDiff line numberDiff line change
@@ -1159,3 +1159,15 @@ lib.fillText = function(calcPt, trace, contOut) {
11591159
lib.isValidTextValue = function(v) {
11601160
return v || v === 0;
11611161
};
1162+
1163+
lib.formatPercent = function(ratio, n) {
1164+
n = n || 0;
1165+
var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%';
1166+
for(var i = 0; i < n; i++) {
1167+
if(str.indexOf('.') !== -1) {
1168+
str = str.replace('0%', '%');
1169+
str = str.replace('.%', '%');
1170+
}
1171+
}
1172+
return str;
1173+
};

Diff for: src/plot_api/helpers.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

1211
var isNumeric = require('fast-isnumeric');
@@ -328,7 +327,7 @@ exports.cleanData = function(data) {
328327
trace.scene = Plots.subplotsRegistry.gl3d.cleanId(trace.scene);
329328
}
330329

331-
if(!traceIs(trace, 'pie') && !traceIs(trace, 'bar-like')) {
330+
if(!traceIs(trace, 'pie-like') && !traceIs(trace, 'bar-like')) {
332331
if(Array.isArray(trace.textposition)) {
333332
for(i = 0; i < trace.textposition.length; i++) {
334333
trace.textposition[i] = cleanTextPosition(trace.textposition[i]);

Diff for: src/plot_api/plot_api.js

+9-5
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

12-
1311
var d3 = require('d3');
1412
var isNumeric = require('fast-isnumeric');
1513
var hasHover = require('has-hover');
@@ -1634,7 +1632,10 @@ function _restyle(gd, aobj, traces) {
16341632
doextra(prefixDot + 'len', innerContFull.len *
16351633
(newVal === 'fraction' ? 1 / lennorm : lennorm), i);
16361634
}
1637-
} else if(ai === 'type' && (newVal === 'pie') !== (oldVal === 'pie')) {
1635+
} else if(ai === 'type' && (
1636+
(newVal === 'pie') !== (oldVal === 'pie') ||
1637+
(newVal === 'funnelarea') !== (oldVal === 'funnelarea')
1638+
)) {
16381639
var labelsTo = 'x';
16391640
var valuesTo = 'y';
16401641
if((newVal === 'bar' || oldVal === 'bar') && cont.orientation === 'h') {
@@ -1645,7 +1646,7 @@ function _restyle(gd, aobj, traces) {
16451646
Lib.swapAttrs(cont, ['d?', '?0'], 'label', labelsTo);
16461647
Lib.swapAttrs(cont, ['?', '?src'], 'values', valuesTo);
16471648

1648-
if(oldVal === 'pie') {
1649+
if(oldVal === 'pie' || oldVal === 'funnelarea') {
16491650
nestedProperty(cont, 'marker.color')
16501651
.set(nestedProperty(cont, 'marker.colors').get());
16511652

@@ -3769,10 +3770,13 @@ function makePlotFramework(gd) {
37693770
// single geo layer for the whole plot
37703771
fullLayout._geolayer = fullLayout._paper.append('g').classed('geolayer', true);
37713772

3773+
// single funnelarea layer for the whole plot
3774+
fullLayout._funnelarealayer = fullLayout._paper.append('g').classed('funnelarealayer', true);
3775+
37723776
// single pie layer for the whole plot
37733777
fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true);
37743778

3775-
// single sunbursrt layer for the whole plot
3779+
// single sunburst layer for the whole plot
37763780
fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true);
37773781

37783782
// fill in image server scrape-svg

Diff for: src/plots/plots.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -2758,9 +2758,10 @@ plots.doCalcdata = function(gd, traces) {
27582758
gd._hmpixcount = 0;
27592759
gd._hmlumcount = 0;
27602760

2761-
// for sharing colors across pies / sunbursts (and for legend)
2761+
// for sharing colors across pies / sunbursts / funnelarea (and for legend)
27622762
fullLayout._piecolormap = {};
27632763
fullLayout._sunburstcolormap = {};
2764+
fullLayout._funnelareacolormap = {};
27642765

27652766
// If traces were specified and this trace was not included,
27662767
// then transfer it over from the old calcdata:

Diff for: src/snapshot/cloneplot.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
109
'use strict';
1110

11+
var Registry = require('../registry');
1212
var Lib = require('../lib');
1313

1414
var extendFlat = Lib.extendFlat;
@@ -89,7 +89,7 @@ module.exports = function clonePlot(graphObj, options) {
8989
var trace = newData[i];
9090
trace.showscale = false;
9191
if(trace.marker) trace.marker.showscale = false;
92-
if(trace.type === 'pie') trace.textposition = 'none';
92+
if(Registry.traceIs(trace, 'pie-like')) trace.textposition = 'none';
9393
}
9494
}
9595

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ module.exports = {
8888
valType: 'enumerated',
8989
values: ['end', 'middle', 'start'],
9090
dflt: 'end',
91-
role: 'info', // TODO: or style ?
91+
role: 'info',
9292
editType: 'plot',
9393
description: [
9494
'Determines if texts are kept at center or start/end points in `textposition` *inside* mode.'
@@ -98,7 +98,7 @@ module.exports = {
9898
textangle: {
9999
valType: 'angle',
100100
dflt: 'auto',
101-
role: 'info', // TODO: or style ?
101+
role: 'info',
102102
editType: 'plot',
103103
description: [
104104
'Sets the angle of the tick labels with respect to the bar.',

0 commit comments

Comments
 (0)