Commit e1e3ddcf authored by Masayuki Tanaka's avatar Masayuki Tanaka

Support multiple lines tick text on category axis - #139

parent 1f4e2228
......@@ -85,6 +85,7 @@ module.exports = (grunt) ->
options:
specs: 'spec/*-spec.js'
helpers: 'spec/*-helper.js'
styles: 'c3.css'
vendor: 'http://d3js.org/d3.v3.min.js'
uglify:
......
......@@ -975,6 +975,7 @@
axis_x_tick_values: null,
axis_x_tick_rotate: undefined,
axis_x_tick_outer: true,
axis_x_tick_width: 80,
axis_x_max: undefined,
axis_x_min: undefined,
axis_x_padding: {},
......@@ -3893,7 +3894,11 @@
};
c3_chart_internal_fn.getXAxis = function (scale, orient, tickFormat, tickValues, withOuterTick) {
var $$ = this, config = $$.config,
axisParams = {isCategory: $$.isCategorized(), withOuterTick: withOuterTick},
axisParams = {
isCategory: $$.isCategorized(),
withOuterTick: withOuterTick,
tickWidth: $$.isCategorized() ? config.axis_x_tick_width : undefined
},
axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient);
if ($$.isTimeSeries() && tickValues) {
......@@ -4129,7 +4134,7 @@
axis = $$.getXAxis(scale, $$.xOrient, $$.getXAxisTickFormat(), $$.getXAxisTickValues());
}
$$.d3.select('body').append("g").style('visibility', 'hidden').call(axis).each(function () {
$$.d3.select(this).selectAll('text').each(function () {
$$.d3.select(this).selectAll('text tspan').each(function () {
var box = this.getBoundingClientRect();
if (box.left > 0 && maxWidth < box.width) { maxWidth = box.width; }
});
......@@ -6343,6 +6348,23 @@
function textFormatted(v) {
return tickFormat ? tickFormat(v) : v;
}
function getSizeFor1Char(tick) {
var size = {
h: 11.5,
w: 5.5
};
tick.select('text').text(textFormatted).each(function (d) {
var box = this.getBoundingClientRect(),
text = textFormatted(d),
h = box.height,
w = text ? (box.width / text.length) : undefined;
if (h && w) {
size.h = h;
size.w = w;
}
}).text('');
return size;
}
function axis(g) {
g.each(function () {
var g = d3.select(this);
......@@ -6364,7 +6386,6 @@
var lineEnter = tickEnter.select("line"),
lineUpdate = tickUpdate.select("line"),
text = tick.select("text").text(textFormatted),
textEnter = tickEnter.select("text"),
textUpdate = tickUpdate.select("text");
......@@ -6376,20 +6397,77 @@
tickOffset = tickX = 0;
}
var text, tspan, sizeFor1Char = getSizeFor1Char(tick), counts = [];
var tickLength = Math.max(innerTickSize, 0) + tickPadding,
isVertical = orient === 'left' || orient === 'right';
// this should be called only when category axis
function splitTickText(d) {
var tickText = textFormatted(d) + "",
maxWidth = isVertical ? params.tickWidth : tickOffset * 2 - 10,
subtext, spaceIndex, textWidth, splitted = [];
function split(splitted, text) {
spaceIndex = undefined;
for (var i = 0; i < text.length; i++) {
if (text.charAt(i) === ' ') {
spaceIndex = i;
}
subtext = text.substr(0, i + 1);
textWidth = sizeFor1Char.w * subtext.length;
// if text width gets over tick width, split by space index or crrent index
if (maxWidth < textWidth) {
return split(
splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)),
text.slice(spaceIndex ? spaceIndex + 1 : i)
);
}
}
return splitted.concat(text);
}
return split(splitted, tickText);
}
function tspanDy(d, i) {
var dy = sizeFor1Char.h;
if (i === 0) {
if (orient === 'left' || orient === 'right') {
dy = -((counts[d.index] - 1) * (sizeFor1Char.h / 2) - (params.isCategory ? 2 : 3));
} else {
dy = params.isCategory ? ".40em" : ".71em";
}
}
return dy;
}
function tickSize(d) {
var tickPosition = scale(d) + tickOffset;
return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0;
}
text = tick.select("text");
tspan = text.selectAll('tspan')
.data(function (d, i) {
var splitted = params.tickWidth ? splitTickText(d) : [textFormatted(d)];
counts[i] = splitted.length;
return splitted.map(function (s) {
return { index: i, splitted: s };
});
})
.enter().append('tspan')
.text(function (d) { return d.splitted; });
switch (orient) {
case "bottom":
{
tickTransform = axisX;
lineEnter.attr("y2", innerTickSize);
textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding);
textEnter.attr("y", tickLength);
lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize);
textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding);
text.attr("dy", ".71em").style("text-anchor", "middle");
textUpdate.attr("x", 0).attr("y", tickLength);
text.style("text-anchor", "middle");
tspan.attr('x', 0).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
break;
}
......@@ -6397,10 +6475,11 @@
{
tickTransform = axisX;
lineEnter.attr("y2", -innerTickSize);
textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
textEnter.attr("y", -tickLength);
lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
text.attr("dy", "0em").style("text-anchor", "middle");
textUpdate.attr("x", 0).attr("y", -tickLength);
text.style("text-anchor", "middle");
tspan.attr('x', 0).attr("dy", "0em");
pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
break;
}
......@@ -6408,10 +6487,11 @@
{
tickTransform = axisY;
lineEnter.attr("x2", -innerTickSize);
textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding));
textEnter.attr("x", -tickLength);
lineUpdate.attr("x2", -innerTickSize).attr("y1", tickY).attr("y2", tickY);
textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", tickOffset);
text.attr("dy", ".32em").style("text-anchor", "end");
textUpdate.attr("x", -tickLength).attr("y", tickOffset);
text.style("text-anchor", "end");
tspan.attr('x', -tickLength).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
break;
}
......@@ -6419,10 +6499,11 @@
{
tickTransform = axisY;
lineEnter.attr("x2", innerTickSize);
textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding);
textEnter.attr("x", tickLength);
lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0);
text.attr("dy", ".32em").style("text-anchor", "start");
textUpdate.attr("x", tickLength).attr("y", 0);
text.style("text-anchor", "start");
tspan.attr('x', tickLength).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
break;
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -34,7 +34,7 @@ describe('c3 chart legend', function () {
it('should be located on the center of chart', function () {
var box = chart.internal.legend.node().getBoundingClientRect();
expect(box.left + box.right).toBe(640);
expect(box.left + box.right).toBe(645);
});
});
......
......@@ -32,7 +32,11 @@ c3_chart_internal_fn.initAxis = function () {
};
c3_chart_internal_fn.getXAxis = function (scale, orient, tickFormat, tickValues, withOuterTick) {
var $$ = this, config = $$.config,
axisParams = {isCategory: $$.isCategorized(), withOuterTick: withOuterTick},
axisParams = {
isCategory: $$.isCategorized(),
withOuterTick: withOuterTick,
tickWidth: $$.isCategorized() ? config.axis_x_tick_width : undefined
},
axis = c3_axis($$.d3, axisParams).scale(scale).orient(orient);
if ($$.isTimeSeries() && tickValues) {
......@@ -268,7 +272,7 @@ c3_chart_internal_fn.getMaxTickWidth = function (id) {
axis = $$.getXAxis(scale, $$.xOrient, $$.getXAxisTickFormat(), $$.getXAxisTickValues());
}
$$.d3.select('body').append("g").style('visibility', 'hidden').call(axis).each(function () {
$$.d3.select(this).selectAll('text').each(function () {
$$.d3.select(this).selectAll('text tspan').each(function () {
var box = this.getBoundingClientRect();
if (box.left > 0 && maxWidth < box.width) { maxWidth = box.width; }
});
......
......@@ -48,6 +48,23 @@ function c3_axis(d3, params) {
function textFormatted(v) {
return tickFormat ? tickFormat(v) : v;
}
function getSizeFor1Char(tick) {
var size = {
h: 11.5,
w: 5.5
};
tick.select('text').text(textFormatted).each(function (d) {
var box = this.getBoundingClientRect(),
text = textFormatted(d),
h = box.height,
w = text ? (box.width / text.length) : undefined;
if (h && w) {
size.h = h;
size.w = w;
}
}).text('');
return size;
}
function axis(g) {
g.each(function () {
var g = d3.select(this);
......@@ -69,7 +86,6 @@ function c3_axis(d3, params) {
var lineEnter = tickEnter.select("line"),
lineUpdate = tickUpdate.select("line"),
text = tick.select("text").text(textFormatted),
textEnter = tickEnter.select("text"),
textUpdate = tickUpdate.select("text");
......@@ -81,20 +97,77 @@ function c3_axis(d3, params) {
tickOffset = tickX = 0;
}
var text, tspan, sizeFor1Char = getSizeFor1Char(tick), counts = [];
var tickLength = Math.max(innerTickSize, 0) + tickPadding,
isVertical = orient === 'left' || orient === 'right';
// this should be called only when category axis
function splitTickText(d) {
var tickText = textFormatted(d) + "",
maxWidth = isVertical ? params.tickWidth : tickOffset * 2 - 10,
subtext, spaceIndex, textWidth, splitted = [];
function split(splitted, text) {
spaceIndex = undefined;
for (var i = 0; i < text.length; i++) {
if (text.charAt(i) === ' ') {
spaceIndex = i;
}
subtext = text.substr(0, i + 1);
textWidth = sizeFor1Char.w * subtext.length;
// if text width gets over tick width, split by space index or crrent index
if (maxWidth < textWidth) {
return split(
splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)),
text.slice(spaceIndex ? spaceIndex + 1 : i)
);
}
}
return splitted.concat(text);
}
return split(splitted, tickText);
}
function tspanDy(d, i) {
var dy = sizeFor1Char.h;
if (i === 0) {
if (orient === 'left' || orient === 'right') {
dy = -((counts[d.index] - 1) * (sizeFor1Char.h / 2) - (params.isCategory ? 2 : 3));
} else {
dy = params.isCategory ? ".40em" : ".71em";
}
}
return dy;
}
function tickSize(d) {
var tickPosition = scale(d) + tickOffset;
return range[0] < tickPosition && tickPosition < range[1] ? innerTickSize : 0;
}
text = tick.select("text");
tspan = text.selectAll('tspan')
.data(function (d, i) {
var splitted = params.tickWidth ? splitTickText(d) : [textFormatted(d)];
counts[i] = splitted.length;
return splitted.map(function (s) {
return { index: i, splitted: s };
});
})
.enter().append('tspan')
.text(function (d) { return d.splitted; });
switch (orient) {
case "bottom":
{
tickTransform = axisX;
lineEnter.attr("y2", innerTickSize);
textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding);
textEnter.attr("y", tickLength);
lineUpdate.attr("x1", tickX).attr("x2", tickX).attr("y2", tickSize);
textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding);
text.attr("dy", ".71em").style("text-anchor", "middle");
textUpdate.attr("x", 0).attr("y", tickLength);
text.style("text-anchor", "middle");
tspan.attr('x', 0).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
break;
}
......@@ -102,10 +175,11 @@ function c3_axis(d3, params) {
{
tickTransform = axisX;
lineEnter.attr("y2", -innerTickSize);
textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
textEnter.attr("y", -tickLength);
lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
text.attr("dy", "0em").style("text-anchor", "middle");
textUpdate.attr("x", 0).attr("y", -tickLength);
text.style("text-anchor", "middle");
tspan.attr('x', 0).attr("dy", "0em");
pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
break;
}
......@@ -113,10 +187,11 @@ function c3_axis(d3, params) {
{
tickTransform = axisY;
lineEnter.attr("x2", -innerTickSize);
textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding));
textEnter.attr("x", -tickLength);
lineUpdate.attr("x2", -innerTickSize).attr("y1", tickY).attr("y2", tickY);
textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", tickOffset);
text.attr("dy", ".32em").style("text-anchor", "end");
textUpdate.attr("x", -tickLength).attr("y", tickOffset);
text.style("text-anchor", "end");
tspan.attr('x', -tickLength).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
break;
}
......@@ -124,10 +199,11 @@ function c3_axis(d3, params) {
{
tickTransform = axisY;
lineEnter.attr("x2", innerTickSize);
textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding);
textEnter.attr("x", tickLength);
lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0);
text.attr("dy", ".32em").style("text-anchor", "start");
textUpdate.attr("x", tickLength).attr("y", 0);
text.style("text-anchor", "start");
tspan.attr('x', tickLength).attr("dy", tspanDy);
pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
break;
}
......
......@@ -92,6 +92,7 @@ c3_chart_internal_fn.getDefaultConfig = function () {
axis_x_tick_values: null,
axis_x_tick_rotate: undefined,
axis_x_tick_outer: true,
axis_x_tick_width: 80,
axis_x_max: undefined,
axis_x_min: undefined,
axis_x_padding: {},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment