Quite good looking graph derived from d3.js http://c3js.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

302 lines
11 KiB

import CLASS from './class';
import { c3_chart_fn, c3_chart_internal_fn } from './core';
import { isValue, isDefined, diffDomain } from './util';
c3_chart_fn.flow = function (args) {
var $$ = this.internal,
targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(),
dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to;
if (args.json) {
data = $$.convertJsonToData(args.json, args.keys);
}
else if (args.rows) {
data = $$.convertRowsToData(args.rows);
}
else if (args.columns) {
data = $$.convertColumnsToData(args.columns);
}
else {
return;
}
targets = $$.convertDataToTargets(data, true);
// Update/Add data
$$.data.targets.forEach(function (t) {
var found = false, i, j;
for (i = 0; i < targets.length; i++) {
if (t.id === targets[i].id) {
found = true;
if (t.values[t.values.length - 1]) {
tail = t.values[t.values.length - 1].index + 1;
}
length = targets[i].values.length;
for (j = 0; j < length; j++) {
targets[i].values[j].index = tail + j;
if (!$$.isTimeSeries()) {
targets[i].values[j].x = tail + j;
}
}
t.values = t.values.concat(targets[i].values);
targets.splice(i, 1);
break;
}
}
if (!found) { notfoundIds.push(t.id); }
});
// Append null for not found targets
$$.data.targets.forEach(function (t) {
var i, j;
for (i = 0; i < notfoundIds.length; i++) {
if (t.id === notfoundIds[i]) {
tail = t.values[t.values.length - 1].index + 1;
for (j = 0; j < length; j++) {
t.values.push({
id: t.id,
index: tail + j,
x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j,
value: null
});
}
}
}
});
// Generate null values for new target
if ($$.data.targets.length) {
targets.forEach(function (t) {
var i, missing = [];
for (i = $$.data.targets[0].values[0].index; i < tail; i++) {
missing.push({
id: t.id,
index: i,
x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i,
value: null
});
}
t.values.forEach(function (v) {
v.index += tail;
if (!$$.isTimeSeries()) {
v.x += tail;
}
});
t.values = missing.concat(t.values);
});
}
$$.data.targets = $$.data.targets.concat(targets); // add remained
// check data count because behavior needs to change when it's only one
dataCount = $$.getMaxDataCount();
baseTarget = $$.data.targets[0];
baseValue = baseTarget.values[0];
// Update length to flow if needed
if (isDefined(args.to)) {
length = 0;
to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to;
baseTarget.values.forEach(function (v) {
if (v.x < to) { length++; }
});
} else if (isDefined(args.length)) {
length = args.length;
}
// If only one data, update the domain to flow from left edge of the chart
if (!orgDataCount) {
if ($$.isTimeSeries()) {
if (baseTarget.values.length > 1) {
diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x;
} else {
diff = baseValue.x - $$.getXDomain($$.data.targets)[0];
}
} else {
diff = 1;
}
domain = [baseValue.x - diff, baseValue.x];
$$.updateXDomain(null, true, true, false, domain);
} else if (orgDataCount === 1) {
if ($$.isTimeSeries()) {
diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2;
domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)];
$$.updateXDomain(null, true, true, false, domain);
}
}
// Set targets
$$.updateTargets($$.data.targets);
// Redraw with new targets
$$.redraw({
flow: {
index: baseValue.index,
length: length,
duration: isValue(args.duration) ? args.duration : $$.config.transition_duration,
done: args.done,
orgDataCount: orgDataCount,
},
withLegend: true,
withTransition: orgDataCount > 1,
withTrimXDomain: false,
withUpdateXAxis: true,
});
};
c3_chart_internal_fn.generateFlow = function (args) {
var $$ = this, config = $$.config, d3 = $$.d3;
return function () {
var targets = args.targets,
flow = args.flow,
drawBar = args.drawBar,
drawLine = args.drawLine,
drawArea = args.drawArea,
cx = args.cx,
cy = args.cy,
xv = args.xv,
xForText = args.xForText,
yForText = args.yForText,
duration = args.duration;
var translateX, scaleX = 1, transform,
flowIndex = flow.index,
flowLength = flow.length,
flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex),
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength),
orgDomain = $$.x.domain(), domain,
durationForFlow = flow.duration || duration,
done = flow.done || function () {},
wait = $$.generateWait();
var xgrid,
xgridLines,
mainRegion,
mainText,
mainBar,
mainLine,
mainArea,
mainCircle;
// set flag
$$.flowing = true;
// remove head data after rendered
$$.data.targets.forEach(function (d) {
d.values.splice(0, flowLength);
});
// update x domain to generate axis elements for flow
domain = $$.updateXDomain(targets, true, true);
// update elements related to x scale
if ($$.updateXGrid) { $$.updateXGrid(true); }
xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid
xgridLines = $$.xgridLines || d3.selectAll([]);
mainRegion = $$.mainRegion || d3.selectAll([]);
mainText = $$.mainText || d3.selectAll([]);
mainBar = $$.mainBar || d3.selectAll([]);
mainLine = $$.mainLine || d3.selectAll([]);
mainArea = $$.mainArea || d3.selectAll([]);
mainCircle = $$.mainCircle || d3.selectAll([]);
// generate transform to flow
if (!flow.orgDataCount) { // if empty
if ($$.data.targets[0].values.length !== 1) {
translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
} else {
if ($$.isTimeSeries()) {
flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0);
flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1);
translateX = $$.x(flowStart.x) - $$.x(flowEnd.x);
} else {
translateX = diffDomain(domain) / 2;
}
}
} else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) {
translateX = $$.x(orgDomain[0]) - $$.x(domain[0]);
} else {
if ($$.isTimeSeries()) {
translateX = ($$.x(orgDomain[0]) - $$.x(domain[0]));
} else {
translateX = ($$.x(flowStart.x) - $$.x(flowEnd.x));
}
}
scaleX = (diffDomain(orgDomain) / diffDomain(domain));
transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)';
$$.hideXGridFocus();
var flowTransition = d3.transition().ease(d3.easeLinear).duration(durationForFlow);
wait.add($$.xAxis($$.axes.x, flowTransition));
wait.add(mainBar.transition(flowTransition).attr('transform', transform));
wait.add(mainLine.transition(flowTransition).attr('transform', transform));
wait.add(mainArea.transition(flowTransition).attr('transform', transform));
wait.add(mainCircle.transition(flowTransition).attr('transform', transform));
wait.add(mainText.transition(flowTransition).attr('transform', transform));
wait.add(mainRegion.filter($$.isRegionOnX).transition(flowTransition).attr('transform', transform));
wait.add(xgrid.transition(flowTransition).attr('transform', transform));
wait.add(xgridLines.transition(flowTransition).attr('transform', transform));
wait(function () {
var i, shapes = [], texts = [];
// remove flowed elements
if (flowLength) {
for (i = 0; i < flowLength; i++) {
shapes.push('.' + CLASS.shape + '-' + (flowIndex + i));
texts.push('.' + CLASS.text + '-' + (flowIndex + i));
}
$$.svg.selectAll('.' + CLASS.shapes).selectAll(shapes).remove();
$$.svg.selectAll('.' + CLASS.texts).selectAll(texts).remove();
$$.svg.select('.' + CLASS.xgrid).remove();
}
// draw again for removing flowed elements and reverting attr
xgrid
.attr('transform', null)
.attr('x1', $$.xgridAttr.x1)
.attr('x2', $$.xgridAttr.x2)
.attr('y1', $$.xgridAttr.y1)
.attr('y2', $$.xgridAttr.y2)
.style("opacity", $$.xgridAttr.opacity);
xgridLines
.attr('transform', null);
xgridLines.select('line')
.attr("x1", config.axis_rotated ? 0 : xv)
.attr("x2", config.axis_rotated ? $$.width : xv);
xgridLines.select('text')
.attr("x", config.axis_rotated ? $$.width : 0)
.attr("y", xv);
mainBar
.attr('transform', null)
.attr("d", drawBar);
mainLine
.attr('transform', null)
.attr("d", drawLine);
mainArea
.attr('transform', null)
.attr("d", drawArea);
mainCircle
.attr('transform', null)
.attr("cx", cx)
.attr("cy", cy);
mainText
.attr('transform', null)
.attr('x', xForText)
.attr('y', yForText)
.style('fill-opacity', $$.opacityForText.bind($$));
mainRegion
.attr('transform', null);
mainRegion.filter($$.isRegionOnX)
.attr("x", $$.regionX.bind($$))
.attr("width", $$.regionWidth.bind($$));
// callback for end of flow
done();
$$.flowing = false;
});
};
};