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.
267 lines
11 KiB
267 lines
11 KiB
1 year ago
|
import CLASS from './class';
|
||
|
import { ChartInternal } from './core';
|
||
|
import { isFunction } from './util';
|
||
|
|
||
|
ChartInternal.prototype.initBrush = function (scale) {
|
||
|
var $$ = this, d3 = $$.d3;
|
||
|
// TODO: dynamically change brushY/brushX according to axis_rotated.
|
||
|
$$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()).on("brush", function () {
|
||
|
var event = d3.event.sourceEvent;
|
||
|
if (event && event.type === "zoom") { return; }
|
||
|
$$.redrawForBrush();
|
||
|
}).on("end", function () {
|
||
|
var event = d3.event.sourceEvent;
|
||
|
if (event && event.type === "zoom") { return; }
|
||
|
if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear(); }
|
||
|
});
|
||
|
$$.brush.updateExtent = function () {
|
||
|
var range = this.scale.range(), extent;
|
||
|
if ($$.config.axis_rotated) {
|
||
|
extent = [[0, range[0]], [$$.width2, range[1]]];
|
||
|
}
|
||
|
else {
|
||
|
extent = [[range[0], 0], [range[1], $$.height2]];
|
||
|
}
|
||
|
this.extent(extent);
|
||
|
return this;
|
||
|
};
|
||
|
$$.brush.updateScale = function (scale) {
|
||
|
this.scale = scale;
|
||
|
return this;
|
||
|
};
|
||
|
$$.brush.update = function (scale) {
|
||
|
this.updateScale(scale || $$.subX).updateExtent();
|
||
|
$$.context.select('.' + CLASS.brush).call(this);
|
||
|
};
|
||
|
$$.brush.clear = function () {
|
||
|
$$.context.select('.' + CLASS.brush).call($$.brush.move, null);
|
||
|
};
|
||
|
$$.brush.selection = function () {
|
||
|
return d3.brushSelection($$.context.select('.' + CLASS.brush).node());
|
||
|
};
|
||
|
$$.brush.selectionAsValue = function (selectionAsValue, withTransition) {
|
||
|
var selection, brush;
|
||
|
if (selectionAsValue) {
|
||
|
if ($$.context) {
|
||
|
selection = [this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1])];
|
||
|
brush = $$.context.select('.' + CLASS.brush);
|
||
|
if (withTransition) { brush = brush.transition(); }
|
||
|
$$.brush.move(brush, selection);
|
||
|
}
|
||
|
return [];
|
||
|
}
|
||
|
selection = $$.brush.selection() || [0,0];
|
||
|
return [this.scale.invert(selection[0]), this.scale.invert(selection[1])];
|
||
|
};
|
||
|
$$.brush.empty = function () {
|
||
|
var selection = $$.brush.selection();
|
||
|
return !selection || selection[0] === selection[1];
|
||
|
};
|
||
|
return $$.brush.updateScale(scale);
|
||
|
};
|
||
|
ChartInternal.prototype.initSubchart = function () {
|
||
|
var $$ = this, config = $$.config,
|
||
|
context = $$.context = $$.svg.append("g").attr("transform", $$.getTranslate('context')),
|
||
|
visibility = config.subchart_show ? 'visible' : 'hidden';
|
||
|
|
||
|
// set style
|
||
|
context.style('visibility', visibility);
|
||
|
|
||
|
// Define g for chart area
|
||
|
context.append('g')
|
||
|
.attr("clip-path", $$.clipPathForSubchart)
|
||
|
.attr('class', CLASS.chart);
|
||
|
|
||
|
// Define g for bar chart area
|
||
|
context.select('.' + CLASS.chart).append("g")
|
||
|
.attr("class", CLASS.chartBars);
|
||
|
|
||
|
// Define g for line chart area
|
||
|
context.select('.' + CLASS.chart).append("g")
|
||
|
.attr("class", CLASS.chartLines);
|
||
|
|
||
|
// Add extent rect for Brush
|
||
|
context.append("g")
|
||
|
.attr("clip-path", $$.clipPath)
|
||
|
.attr("class", CLASS.brush);
|
||
|
|
||
|
// ATTENTION: This must be called AFTER chart added
|
||
|
// Add Axis
|
||
|
$$.axes.subx = context.append("g")
|
||
|
.attr("class", CLASS.axisX)
|
||
|
.attr("transform", $$.getTranslate('subx'))
|
||
|
.attr("clip-path", config.axis_rotated ? "" : $$.clipPathForXAxis);
|
||
|
};
|
||
|
ChartInternal.prototype.initSubchartBrush = function () {
|
||
|
var $$ = this;
|
||
|
// Add extent rect for Brush
|
||
|
$$.initBrush($$.subX).updateExtent();
|
||
|
$$.context.select('.' + CLASS.brush).call($$.brush);
|
||
|
};
|
||
|
ChartInternal.prototype.updateTargetsForSubchart = function (targets) {
|
||
|
var $$ = this, context = $$.context, config = $$.config,
|
||
|
contextLineEnter, contextLine, contextBarEnter, contextBar,
|
||
|
classChartBar = $$.classChartBar.bind($$),
|
||
|
classBars = $$.classBars.bind($$),
|
||
|
classChartLine = $$.classChartLine.bind($$),
|
||
|
classLines = $$.classLines.bind($$),
|
||
|
classAreas = $$.classAreas.bind($$);
|
||
|
|
||
|
if (config.subchart_show) {
|
||
|
//-- Bar --//
|
||
|
contextBar = context.select('.' + CLASS.chartBars).selectAll('.' + CLASS.chartBar)
|
||
|
.data(targets);
|
||
|
contextBarEnter = contextBar.enter().append('g')
|
||
|
.style('opacity', 0);
|
||
|
contextBarEnter.merge(contextBar)
|
||
|
.attr('class', classChartBar);
|
||
|
// Bars for each data
|
||
|
contextBarEnter.append('g')
|
||
|
.attr("class", classBars);
|
||
|
|
||
|
//-- Line --//
|
||
|
contextLine = context.select('.' + CLASS.chartLines).selectAll('.' + CLASS.chartLine)
|
||
|
.data(targets);
|
||
|
contextLineEnter = contextLine.enter().append('g')
|
||
|
.style('opacity', 0);
|
||
|
contextLineEnter.merge(contextLine)
|
||
|
.attr('class', classChartLine);
|
||
|
// Lines for each data
|
||
|
contextLineEnter.append("g")
|
||
|
.attr("class", classLines);
|
||
|
// Area
|
||
|
contextLineEnter.append("g")
|
||
|
.attr("class", classAreas);
|
||
|
|
||
|
//-- Brush --//
|
||
|
context.selectAll('.' + CLASS.brush + ' rect')
|
||
|
.attr(config.axis_rotated ? "width" : "height", config.axis_rotated ? $$.width2 : $$.height2);
|
||
|
}
|
||
|
};
|
||
|
ChartInternal.prototype.updateBarForSubchart = function (durationForExit) {
|
||
|
var $$ = this;
|
||
|
var contextBar = $$.context.selectAll('.' + CLASS.bars).selectAll('.' + CLASS.bar)
|
||
|
.data($$.barData.bind($$));
|
||
|
var contextBarEnter = contextBar.enter().append('path')
|
||
|
.attr("class", $$.classBar.bind($$))
|
||
|
.style("stroke", 'none')
|
||
|
.style("fill", $$.color);
|
||
|
contextBar.exit().transition().duration(durationForExit)
|
||
|
.style('opacity', 0)
|
||
|
.remove();
|
||
|
$$.contextBar = contextBarEnter.merge(contextBar)
|
||
|
.style("opacity", $$.initialOpacity.bind($$));
|
||
|
};
|
||
|
ChartInternal.prototype.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) {
|
||
|
(withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar)
|
||
|
.attr('d', drawBarOnSub)
|
||
|
.style('opacity', 1);
|
||
|
};
|
||
|
ChartInternal.prototype.updateLineForSubchart = function (durationForExit) {
|
||
|
var $$ = this;
|
||
|
var contextLine = $$.context.selectAll('.' + CLASS.lines).selectAll('.' + CLASS.line)
|
||
|
.data($$.lineData.bind($$));
|
||
|
var contextLineEnter = contextLine.enter().append('path')
|
||
|
.attr('class', $$.classLine.bind($$))
|
||
|
.style('stroke', $$.color);
|
||
|
contextLine.exit().transition().duration(durationForExit)
|
||
|
.style('opacity', 0)
|
||
|
.remove();
|
||
|
$$.contextLine = contextLineEnter.merge(contextLine)
|
||
|
.style("opacity", $$.initialOpacity.bind($$));
|
||
|
};
|
||
|
ChartInternal.prototype.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) {
|
||
|
(withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine)
|
||
|
.attr("d", drawLineOnSub)
|
||
|
.style('opacity', 1);
|
||
|
};
|
||
|
ChartInternal.prototype.updateAreaForSubchart = function (durationForExit) {
|
||
|
var $$ = this, d3 = $$.d3;
|
||
|
var contextArea = $$.context.selectAll('.' + CLASS.areas).selectAll('.' + CLASS.area)
|
||
|
.data($$.lineData.bind($$));
|
||
|
var contextAreaEnter = contextArea.enter().append('path')
|
||
|
.attr("class", $$.classArea.bind($$))
|
||
|
.style("fill", $$.color)
|
||
|
.style("opacity", function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; });
|
||
|
contextArea.exit().transition().duration(durationForExit)
|
||
|
.style('opacity', 0)
|
||
|
.remove();
|
||
|
$$.contextArea = contextAreaEnter.merge(contextArea)
|
||
|
.style("opacity", 0);
|
||
|
};
|
||
|
ChartInternal.prototype.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) {
|
||
|
(withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea)
|
||
|
.attr("d", drawAreaOnSub)
|
||
|
.style("fill", this.color)
|
||
|
.style("opacity", this.orgAreaOpacity);
|
||
|
};
|
||
|
ChartInternal.prototype.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) {
|
||
|
var $$ = this, d3 = $$.d3, config = $$.config,
|
||
|
drawAreaOnSub, drawBarOnSub, drawLineOnSub;
|
||
|
|
||
|
$$.context.style('visibility', config.subchart_show ? 'visible' : 'hidden');
|
||
|
|
||
|
// subchart
|
||
|
if (config.subchart_show) {
|
||
|
// reflect main chart to extent on subchart if zoomed
|
||
|
if (d3.event && d3.event.type === 'zoom') {
|
||
|
$$.brush.selectionAsValue($$.x.orgDomain());
|
||
|
}
|
||
|
// update subchart elements if needed
|
||
|
if (withSubchart) {
|
||
|
// extent rect
|
||
|
if (!$$.brush.empty()) {
|
||
|
$$.brush.selectionAsValue($$.x.orgDomain());
|
||
|
}
|
||
|
// setup drawer - MEMO: this must be called after axis updated
|
||
|
drawAreaOnSub = $$.generateDrawArea(areaIndices, true);
|
||
|
drawBarOnSub = $$.generateDrawBar(barIndices, true);
|
||
|
drawLineOnSub = $$.generateDrawLine(lineIndices, true);
|
||
|
|
||
|
$$.updateBarForSubchart(duration);
|
||
|
$$.updateLineForSubchart(duration);
|
||
|
$$.updateAreaForSubchart(duration);
|
||
|
|
||
|
$$.redrawBarForSubchart(drawBarOnSub, duration, duration);
|
||
|
$$.redrawLineForSubchart(drawLineOnSub, duration, duration);
|
||
|
$$.redrawAreaForSubchart(drawAreaOnSub, duration, duration);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
ChartInternal.prototype.redrawForBrush = function () {
|
||
|
var $$ = this, x = $$.x, d3 = $$.d3, s;
|
||
|
$$.redraw({
|
||
|
withTransition: false,
|
||
|
withY: $$.config.zoom_rescale,
|
||
|
withSubchart: false,
|
||
|
withUpdateXDomain: true,
|
||
|
withEventRect: false,
|
||
|
withDimension: false
|
||
|
});
|
||
|
// update zoom transation binded to event rect
|
||
|
s = d3.event.selection || $$.brush.scale.range();
|
||
|
$$.main.select('.' + CLASS.eventRect).call($$.zoom.transform, d3.zoomIdentity
|
||
|
.scale($$.width / (s[1] - s[0]))
|
||
|
.translate(-s[0], 0));
|
||
|
$$.config.subchart_onbrush.call($$.api, x.orgDomain());
|
||
|
};
|
||
|
ChartInternal.prototype.transformContext = function (withTransition, transitions) {
|
||
|
var $$ = this, subXAxis;
|
||
|
if (transitions && transitions.axisSubX) {
|
||
|
subXAxis = transitions.axisSubX;
|
||
|
} else {
|
||
|
subXAxis = $$.context.select('.' + CLASS.axisX);
|
||
|
if (withTransition) { subXAxis = subXAxis.transition(); }
|
||
|
}
|
||
|
$$.context.attr("transform", $$.getTranslate('context'));
|
||
|
subXAxis.attr("transform", $$.getTranslate('subx'));
|
||
|
};
|
||
|
ChartInternal.prototype.getDefaultSelection = function () {
|
||
|
var $$ = this, config = $$.config,
|
||
|
selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection;
|
||
|
if ($$.isTimeSeries()) {
|
||
|
selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])];
|
||
|
}
|
||
|
return selection;
|
||
|
};
|