diff --git a/Gruntfile.coffee b/Gruntfile.coffee index f6dc622..219576a 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -72,6 +72,7 @@ module.exports = (grunt) -> 'src/api.legend.js', 'src/api.chart.js', 'src/api.tooltip.js', + 'src/api.pie.js', 'src/c3.axis.js', 'src/ua.js', 'src/polyfill.js', diff --git a/spec/api.pie-spec.js b/spec/api.pie-spec.js new file mode 100644 index 0000000..a34f8ee --- /dev/null +++ b/spec/api.pie-spec.js @@ -0,0 +1,34 @@ +describe('c3 api pie', function () { + 'use strict'; + + var chart; + + var args = { + data: { + columns: [ + ['data1', 30], + ['data2', 1000], + ['data3', 5000] + ] + }, + type: 'pie', + pie: { + explodeRadius: 10 + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('explodeRadius', function(){ + it('should get explode radius', function(){ + expect(chart.pie.explodeRadius()).toBe(10); + }); + + it('should set explode radius', function(){ + chart.pie.explodeRadius(20); + expect(chart.pie.explodeRadius()).toBe(20); + }); + }); +}); \ No newline at end of file diff --git a/spec/arc-spec.js b/spec/arc-spec.js index f363693..0940bdc 100644 --- a/spec/arc-spec.js +++ b/spec/arc-spec.js @@ -156,4 +156,130 @@ describe('c3 chart arc', function () { }); }); + describe('explodeRadius', function(){ + it('should set args with explodeRadius', function(){ + args = { + data: { + columns: [ + ['data1', null], + ['data2', null], + ['data3', null] + ], + type: 'pie' + }, + pie:{ + explodeRadius: 10 + } + }; + expect(true).toBeTruthy(); + }); + + it('should have correct d attribute', function () { + var chartArc = d3.select('.c3-chart-arcs'), + arcs = { + data1: chartArc.select('.c3-chart-arc.c3-target.c3-target-data1') + .select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'), + data2: chartArc.select('.c3-chart-arc.c3-target.c3-target-data2') + .select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'), + data3: chartArc.select('.c3-chart-arc.c3-target.c3-target-data3') + .select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3') + }; + expect(arcs.data1.attr('d').indexOf('NaN')).toBe(-1); + expect(arcs.data2.attr('d').indexOf('NaN')).toBe(-1); + expect(arcs.data3.attr('d').indexOf('NaN')).toBe(-1); + }); + + it('should have the usual behaviout with explodeRadius set to 0', function () { + args = { + data: { + columns: [ + ['data1', 30], + ['data2', 150], + ['data3', 120] + ], + type: 'pie' + }, + pie: { + explodeRadius: 0 + } + }; + expect(true).toBeTruthy(); + }); + + it('should have correct classes', function () { + var chartArc = d3.select('.c3-chart-arcs'), + arcs = { + data1: chartArc.select('.c3-chart-arc.c3-target.c3-target-data1') + .select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'), + data2: chartArc.select('.c3-chart-arc.c3-target.c3-target-data2') + .select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'), + data3: chartArc.select('.c3-chart-arc.c3-target.c3-target-data3') + .select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3') + }; + expect(arcs.data1.size()).toBe(1); + expect(arcs.data2.size()).toBe(1); + expect(arcs.data3.size()).toBe(1); + }); + + it('should have correct d', function () { + expect(d3.select('.c3-arc-data1').attr('d')).toMatch(/M-124\..+,-171\..+A211\..+,211\..+ 0 0,1 -3\..+,-211\..+L0,0Z/); + expect(d3.select('.c3-arc-data2').attr('d')).toMatch(/M1\..+,-211\..+A211\..+,211\..+ 0 0,1 1\..+,211\..+L0,0Z/); + expect(d3.select('.c3-arc-data3').attr('d')).toMatch(/M1\..+,211\..+A211\..+,211\..+ 0 0,1 -124\..+,-171\..+L0,0Z/); + }); + + it('should set args with data id that can be converted to a color', function () { + args.data.columns = [ + ['black', 30], + ['data2', 150], + ['data3', 120] + ]; + expect(true).toBeTruthy(); + }); + + it('should have correct d even if data id can be converted to a color', function (done) { + setTimeout(function () { + expect(d3.select('.c3-arc-black').attr('d')).toMatch(/M-124\..+,-171\..+A211\..+,211\..+ 0 0,1 -3\..+,-211\..+L0,0Z/); + done(); + }, 500); + }); + + it('should update args to have empty pie chart', function () { + args = { + data: { + columns: [ + ['data1', null], + ['data2', null], + ['data3', null] + ], + type: 'pie' + } + }; + expect(true).toBeTruthy(); + }); + + it('should have correct d attribute', function () { + var chartArc = d3.select('.c3-chart-arcs'), + arcs = { + data1: chartArc.select('.c3-chart-arc.c3-target.c3-target-data1') + .select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'), + data2: chartArc.select('.c3-chart-arc.c3-target.c3-target-data2') + .select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'), + data3: chartArc.select('.c3-chart-arc.c3-target.c3-target-data3') + .select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3') + }; + expect(arcs.data1.attr('d').indexOf('NaN')).toBe(-1); + expect(arcs.data2.attr('d').indexOf('NaN')).toBe(-1); + expect(arcs.data3.attr('d').indexOf('NaN')).toBe(-1); + }); + }); + }); diff --git a/src/api.pie.js b/src/api.pie.js new file mode 100644 index 0000000..033d96e --- /dev/null +++ b/src/api.pie.js @@ -0,0 +1,12 @@ +c3_chart_fn.pie = function (){}; +c3_chart_fn.pie.explodeRadius = function (radius) { + var $$ = this.internal, config = $$.config + + if(radius === undefined){ + return config.pie_explodeRadius; + } + + config.pie_explodeRadius = radius; + + this.flush(); +}; \ No newline at end of file diff --git a/src/arc.js b/src/arc.js index 5abcdde..d0816fd 100644 --- a/src/arc.js +++ b/src/arc.js @@ -13,6 +13,11 @@ c3_chart_internal_fn.updateRadius = function () { w = config.gauge_width || config.donut_width; $$.radiusExpanded = Math.min($$.arcWidth, $$.arcHeight) / 2; $$.radius = $$.radiusExpanded * 0.95; + + // Because we are exploding pie + // outer radius might become bigger that it should be + $$.radius -= config.pie_explodeRadius; + $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6; $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0; }; @@ -289,7 +294,7 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf this._current = d; }); mainArc - .attr("transform", function (d) { return !$$.isGaugeType(d.data) && withTransform ? "scale(0)" : ""; }) + .attr("transform", $$.wrapExplode()) .style("opacity", function (d) { return d === this._current ? 0 : 1; }) .on('mouseover', config.interaction_enabled ? function (d) { var updated, arcData; @@ -367,7 +372,7 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf return $$.getArc(interpolated, true); }; }) - .attr("transform", withTransform ? "scale(1)" : "") + .attr("transform", $$.wrapExplode()) .style("fill", function (d) { return $$.levelColor ? $$.levelColor(d.data.values[0].value) : $$.color(d.data.id); }) // Where gauge reading color would receive customization. @@ -412,6 +417,35 @@ c3_chart_internal_fn.redrawArc = function (duration, durationForExit, withTransf .text(config.gauge_label_show ? config.gauge_max : ''); } }; + +// This is needed because d3 binds transform function to DOM element, +// thus we can't just pass c3_chart_internal_fn.explode +// but need to close it against c3_chart_internal_fn +c3_chart_internal_fn.wrapExplode = function(){ + var $$ = this, config = $$.config; + + return function explode(d) { + d = $$.updateAngle(d); + + if(d === null){ + return ""; + } + + var offset = config.pie_explodeRadius; + + if(offset <= 0){ + return "translate(0, 0)"; + } + + var angle = (d.startAngle + d.endAngle) / 2; + var xOff = Math.sin(angle) * offset; + var yOff = -Math.cos(angle) * offset; + + return 'translate(' + xOff + ',' + yOff +')'; + }; + +}; + c3_chart_internal_fn.initGauge = function () { var arcs = this.arcs; if (this.hasType('gauge')) { diff --git a/src/config.js b/src/config.js index 6899cbd..9c489f3 100644 --- a/src/config.js +++ b/src/config.js @@ -177,6 +177,7 @@ c3_chart_internal_fn.getDefaultConfig = function () { pie_label_ratio: undefined, pie_expand: {}, pie_expand_duration: 50, + pie_explodeRadius: 0, // gauge gauge_fullCircle: false, gauge_label_show: true,