From a60515afc2374c9c2a76e7d00e2a01a83d89795a Mon Sep 17 00:00:00 2001 From: David Poore Date: Thu, 26 Feb 2015 15:40:13 -0700 Subject: [PATCH] 1033 change legend item tiles to customizable lines instead of static rects --- c3.js | 38 +++++++++++++++------------- spec/api.data-spec.js | 4 +-- spec/legend-spec.js | 58 +++++++++++++++++++++++++++++++++++++++++++ src/config.js | 3 +++ src/legend.js | 35 +++++++++++++------------- 5 files changed, 102 insertions(+), 36 deletions(-) diff --git a/c3.js b/c3.js index 233d1b5..b6e18b8 100644 --- a/c3.js +++ b/c3.js @@ -1092,6 +1092,9 @@ legend_item_onmouseover: undefined, legend_item_onmouseout: undefined, legend_equally: false, + legend_padding: 0, + legend_item_tile_width: 10, + legend_item_tile_height: 10, // axis axis_rotated: false, axis_x_show: true, @@ -3937,8 +3940,8 @@ }; c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { var $$ = this, config = $$.config; - var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect; - var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = 15; + var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; + var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5; var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; var withTransition, withTransitionForTransform; var texts, rects, tiles, background; @@ -3957,7 +3960,7 @@ function updatePositions(textElement, id, index) { var reset = index === 0, isLast = index === targetIds.length - 1, box = getTextBox(textElement, id), - itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight), + itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, itemHeight = box.height + paddingTop, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), @@ -4030,10 +4033,13 @@ xForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; yForLegend = function (id) { return maxHeight * steps[id]; }; } - xForLegendText = function (id, i) { return xForLegend(id, i) + 14; }; + xForLegendText = function (id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width; }; yForLegendText = function (id, i) { return yForLegend(id, i) + 9; }; xForLegendRect = function (id, i) { return xForLegend(id, i); }; yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; }; + x1ForLegendTile = function (id, i) { return xForLegend(id, i) - 2; }; + x2ForLegendTile = function (id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width; }; + yForLegendTile = function (id, i) { return yForLegend(id, i) + 4; }; // Define g for legend area l = $$.legend.selectAll('.' + CLASS.legendItem) @@ -4082,14 +4088,10 @@ .style('fill-opacity', 0) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); - l.append('rect') - .attr("class", CLASS.legendItemTile) + l.append('line') + .attr('class', CLASS.legendItemTile) .style("pointer-events", "none") - .style('fill', $$.color) - .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) - .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegend) - .attr('width', 10) - .attr('height', 10); + .attr('stroke-width', config.legend_item_tile_height); // Set background for inset legend background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); @@ -4115,12 +4117,14 @@ .attr('x', xForLegendRect) .attr('y', yForLegendRect); - tiles = $$.legend.selectAll('rect.' + CLASS.legendItemTile) - .data(targetIds); - (withTransition ? tiles.transition() : tiles) - .style('fill', $$.color) - .attr('x', xForLegend) - .attr('y', yForLegend); + tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile) + .data(targetIds); + (withTransition ? tiles.transition() : tiles) + .style('stroke', $$.color) + .attr('x1', x1ForLegendTile) + .attr('y1', yForLegendTile) + .attr('x2', x2ForLegendTile) + .attr('y2', yForLegendTile); if (background) { (withTransition ? background.transition() : background) diff --git a/spec/api.data-spec.js b/spec/api.data-spec.js index 60881cb..56f5fef 100644 --- a/spec/api.data-spec.js +++ b/spec/api.data-spec.js @@ -132,8 +132,8 @@ describe('c3 api data', function () { it('should set data.colors specified as api', function () { expect(d3.select('.c3-line-data1').style('stroke')).toBe("#00ff00"); expect(d3.select('.c3-line-data2').style('stroke')).toBe("#ff0000"); - expect(d3.select('.c3-legend-item-data1 .c3-legend-item-tile').style('fill')).toBe("#00ff00"); - expect(d3.select('.c3-legend-item-data2 .c3-legend-item-tile').style('fill')).toBe("#ff0000"); + expect(d3.select('.c3-legend-item-data1 .c3-legend-item-tile').style('stroke')).toBe("#00ff00"); + expect(d3.select('.c3-legend-item-data2 .c3-legend-item-tile').style('stroke')).toBe("#ff0000"); }); }); diff --git a/spec/legend-spec.js b/spec/legend-spec.js index 5773b1a..7fb9878 100644 --- a/spec/legend-spec.js +++ b/spec/legend-spec.js @@ -217,4 +217,62 @@ describe('c3 chart legend', function () { }); + describe('custom legend size', function() { + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 130, 100, 200, 100, 250, 150] + ] + }, + legend: { + item: { + tile: { + width: 15, + height: 2 + } + } + } + }; + expect(true).toBeTruthy(); + }); + + it('renders the legend item with the correct width and height', function () { + d3.selectAll('.c3-legend-item-tile').each(function () { + expect(d3.select(this).style('stroke-width')).toBe(args.legend.item.tile.height + 'px'); + var tileWidth = d3.select(this).attr('x2') - d3.select(this).attr('x1'); + expect(tileWidth).toBe(args.legend.item.tile.width); + }); + }); + }); + + describe('custom legend padding', function() { + it('should update args', function () { + args = { + data: { + columns: [ + ['padded1', 30, 200, 100, 400, 150, 250], + ['padded2', 130, 100, 200, 100, 250, 150] + ] + }, + legend: { + padding: 10 + } + }; + expect(true).toBeTruthy(); + }); + + it('renders the correct amount of padding on the legend element', function () { + d3.selectAll('.c3-legend-item-padded1 .c3-legend-item-tile, .c3-legend-item-padded2 .c3-legend-item-tile').each(function (el, index) { + var itemWidth = d3.select(this).node().parentNode.getBBox().width, + textBoxWidth = d3.select(d3.select(this).node().parentNode).select('text').node().getBBox().width, + tileWidth = 15, // default value is 10, plus 5 more for padding + expectedWidth = textBoxWidth + tileWidth + (index ? 0 : 10) + args.legend.padding; + + expect(itemWidth).toBe(expectedWidth); + }); + }); + }); + }); diff --git a/src/config.js b/src/config.js index 737ce1b..6c52861 100644 --- a/src/config.js +++ b/src/config.js @@ -78,6 +78,9 @@ c3_chart_internal_fn.getDefaultConfig = function () { legend_item_onmouseover: undefined, legend_item_onmouseout: undefined, legend_equally: false, + legend_padding: 0, + legend_item_tile_width: 10, + legend_item_tile_height: 10, // axis axis_rotated: false, axis_x_show: true, diff --git a/src/legend.js b/src/legend.js index 7353a98..2e0508e 100644 --- a/src/legend.js +++ b/src/legend.js @@ -113,8 +113,8 @@ c3_chart_internal_fn.clearLegendItemTextBoxCache = function () { }; c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { var $$ = this, config = $$.config; - var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect; - var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = 15; + var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; + var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5; var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; var withTransition, withTransitionForTransform; var texts, rects, tiles, background; @@ -133,7 +133,7 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { function updatePositions(textElement, id, index) { var reset = index === 0, isLast = index === targetIds.length - 1, box = getTextBox(textElement, id), - itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight), + itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, itemHeight = box.height + paddingTop, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), @@ -206,10 +206,13 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { xForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; yForLegend = function (id) { return maxHeight * steps[id]; }; } - xForLegendText = function (id, i) { return xForLegend(id, i) + 14; }; + xForLegendText = function (id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width; }; yForLegendText = function (id, i) { return yForLegend(id, i) + 9; }; xForLegendRect = function (id, i) { return xForLegend(id, i); }; yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; }; + x1ForLegendTile = function (id, i) { return xForLegend(id, i) - 2; }; + x2ForLegendTile = function (id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width; }; + yForLegendTile = function (id, i) { return yForLegend(id, i) + 4; }; // Define g for legend area l = $$.legend.selectAll('.' + CLASS.legendItem) @@ -258,14 +261,10 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { .style('fill-opacity', 0) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); - l.append('rect') - .attr("class", CLASS.legendItemTile) + l.append('line') + .attr('class', CLASS.legendItemTile) .style("pointer-events", "none") - .style('fill', $$.color) - .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) - .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegend) - .attr('width', 10) - .attr('height', 10); + .attr('stroke-width', config.legend_item_tile_height); // Set background for inset legend background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); @@ -291,12 +290,14 @@ c3_chart_internal_fn.updateLegend = function (targetIds, options, transitions) { .attr('x', xForLegendRect) .attr('y', yForLegendRect); - tiles = $$.legend.selectAll('rect.' + CLASS.legendItemTile) - .data(targetIds); - (withTransition ? tiles.transition() : tiles) - .style('fill', $$.color) - .attr('x', xForLegend) - .attr('y', yForLegend); + tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile) + .data(targetIds); + (withTransition ? tiles.transition() : tiles) + .style('stroke', $$.color) + .attr('x1', x1ForLegendTile) + .attr('y1', yForLegendTile) + .attr('x2', x2ForLegendTile) + .attr('y2', yForLegendTile); if (background) { (withTransition ? background.transition() : background)