import {dispatch} from 'd3';
import Facet from '../facet/';
import brushMixin from '../brushMixin';
import fitLineMixin from '../fitLineMixin';
import fixLineMixin from '../fixLineMixin';
import seriesMixin from '../seriesMixin';
import zoomMixin from '../zoomMixin';
import paddingMixin from '../paddingMixin';
import shapeMixin from '../shapeMixin';
import stackMixin from '../stackMixin';
import streamMixin from '../streamMixin';
import {mixedMeasure} from '../../modules/measureField';
import {attrFunc, genFunc, mix} from '../../modules/util';
import _brush from './_brush';
import _brushZoom from './_brushZoom';
import _mark from './_mark';
import _munge from './_munge';
import _panning from './_panning';
import _domain from './_domain';
import _range from './_range';
import _axis from './_axis';
import _meanLine from './_meanLine';
import _legend from './_legend';
import _region from './_region';
import _facet from './_facet';
import _fitLine from './_fitLine';
import _fixLine from './_fixLine';
import _tooltip from './_tooltip';
import _zoom from './_zoom';
import {leastSquare as lsFunc} from '../../modules/transform';
const size = {range: [2, 2], scale: 'linear', reverse: false};
const shapes = ['line', 'area'];
const conditions = ['normal', 'count', 'mixed'];
const _attrs = {
meanLine : false,
multiTooltip: false,
padding: 0,
pointRatio : 2,
regionPadding: 0.1,
shape: shapes[0],
areaGradient: false,
scaleBandMode : false,
size: size,
individualScale: false
};
/**
* renders a line chart
* @class Line
* @augments Core
* @augments RectLinear
* @augments Facet
* @augments FitLineMixin
* @augments SeriesMixin
* @augments BrushMixin
* @augments ZoomMixin
* @augments PaddingMixin
* @augments ShapeMixin
* @augments StreamMixin
*/
class Line extends mix(Facet).with(fitLineMixin, fixLineMixin, seriesMixin, brushMixin, zoomMixin, paddingMixin, shapeMixin, stackMixin, streamMixin) {
constructor() {
super();
this.setAttrs(_attrs);
this.__execs__.multiTooltipDispatch = dispatch('selectStart', 'selectMove', 'selectEnd', 'multiTooltip');
this.rebindOnMethod(this.__execs__.multiTooltipDispatch);
this.process('munge', _munge, {isPre: true})
.process('brushZoom', _brushZoom, {isPre: true, allow: function() {return this.isBrushZoom()}})
.process('domain', _domain, {isPre: true, allow: function() {return !this.isBrushZoom()}})
.process('range', _range, {isPre: true, allow: function() {return !this.isBrushZoom()}})
.process('axis', _axis, {allow: function() {return !this.isBrushZoom()}})
.process('region', _region, {allow: function() {return !this.isBrushZoom()}})
.process('facet', _facet, {allow: function() {return !this.isBrushZoom() && this.isFacet()}})
.process('mark', _mark, {allow: function() {return !this.isBrushZoom() && !this.isFacet()}})
.process('meanLine', _meanLine, {allow: function() {return !this.isBrushZoom()}})
.process('fitLine', _fitLine, {allow: function() {return !this.isBrushZoom()}})
.process('fixLine', _fixLine, {allow: function() {return !this.isBrushZoom()}})
.process('tooltip', _tooltip, {allow: function() {return !this.isBrushZoom()}})
.process('panning', _panning, {allow: function() {return !this.isBrushZoom()}})
.process('zoom', _zoom, {allow: function() {return !this.isBrushZoom()}})
.process('brush', _brush, {allow: function() {return !this.isBrushZoom()}})
.process('legend', _legend, {allow: function() {return !this.isBrushZoom()}})
}
/**
* @override
*/
renderCanvas() {
return super.renderCanvas(this.point() ? this.size().range[0]*2 : 0);
}
/**
* If is true, renders the tooltip showing multiple points on the same horizontal position. If is a string or object, sets sorting order of items by each value. If multiTooltip is not specified, returns the instance's multiTooltip setting.
* @example
* line.multiTooltip(true) // show multiple points on the same horizontal position on a tooltip
* line.multiTooltip('ascending') //sort items in ascending order by their each value
* line.multiTooltip(false)
* line.multiTooltip()
* @param {boolean|string|object} [multiTooltip=false]
* @param {string} [multiTooltip.sortByValue=natural] (natural|ascending|descending)
* @return {multiTooltip|Line}
*/
multiTooltip(multiTooltip) {
if (!arguments.length) return this.__attrs__.multiTooltip;
if (typeof multiTooltip === 'boolean') {
if (multiTooltip) {
multiTooltip = {sortByValue: 'natural'};
}
}
if (typeof multiTooltip === 'object') {
if (!multiTooltip.sortByValue) multiTooltip.sortByValue = 'natural';
}
this.__attrs__.multiTooltip = multiTooltip;
return this;
}
/**
* gets a result of linear least squres from serieses. If key is specified, returns the value only froma specific series. It is used for draw fit-lines.
* @param {string} [key] a name of a series
* @example
* let l = line.data([
* {name: 'A', sales: 10, profit: 5},
* {name: 'B', sales: 20, profit: 10},
* {name: 'C', sales: 30, profit: 3},
* ...
* ]) //sets data
* .dimensions(['name'])
* .measures(['sales', 'profit'])
* .render();
* l.leastSquare('A') // returns a result of the series A
* l.leastSquare() // returns from all serieses
* @return {object[]} returns [{key: fitLineVal, slope, intercept, rSquare}...]
*/
leastSquare(key) {
const measureName = this.measureName();
const individualScale = this.isIndividualScale();
return this.__execs__.munged.filter(series => {
if (typeof key === 'string') {
return series.data.key === key;
} else {
return true;
}
}).map(series => {
let targets = series.children.map(d => {return {x: d.data.key, y:d.data.value[measureName]}});
let ls = lsFunc(targets);
if (individualScale && series.scale) {
ls.scale = series.scale;
}
ls.key = series.data.key;
return ls;
});
}
measureName() {
let measures = this.measures();
let yField;
if (this.condition() === conditions[2]) yField = mixedMeasure.field;
else if (this.aggregated() && measures[0].field === mixedMeasure.field) yField = measures[0].field;
else yField = measures[0].field + '-' + measures[0].op;
return yField;
}
isCount() {
return this.condition() === conditions[1];
}
isFacet() {
return this.facet() && this.isNested() && !this.stacked();
}
isIndividualScale() {
return this.individualScale() && this.isNested() && !this.stacked();
}
isMixed() {
return this.condition() == conditions[2] ;
}
isNested() {
let dimensions = this.dimensions();
let condition = this.condition();
return dimensions.length === 2 || (condition == conditions[2] && dimensions.length === 1);
}
isStacked() {
return this.stacked() && this.isNested();
}
muteFromLegend(legend) {
this.muteRegions(legend.key);
}
muteToLegend(d) {
this.muteLegend(d.parent.data.key);
}
demuteFromLegend(legend) {
this.demuteRegions(legend.key);
}
demuteToLegend(d) {
this.demuteLegend(d.parent.data.key);
}
showMultiTooltip(tick, start) { //for the facet condition
if (this.multiTooltip()) {
let mt = this.__execs__.tooltip;
mt.tick(tick, start);
}
}
}
/**
* If meanLine is specified sets the meanLine setting and returns the Line instance itself. If meanLine is true renders a mean-line on each series. If meanLine is not specified, returns the current meanLine setting.
* @function
* @example
* line.meanLine(true)
* @param {boolean} [meanLine=false] If is true, renders a mean-line.
* @return {meanLine|Line}
*/
Line.prototype.meanLine = attrFunc('meanLine');
Line.prototype.scaleBandMode = attrFunc('scaleBandMode');
/**
* If individualScale is specified sets the individualScale setting and returns the Line instance itself. When a line chart has multiple measures, each measure will be a series. If individualScale is true, when has multiple measures, each series will be drawn based on an individual scale of itself.
* @function
* @example
* line.individualScale(true)
* @param {boolean} [individualScale=false] If is true, renders a mean-line.
* @return {individualScale|Line}
*/
Line.prototype.individualScale = attrFunc('individualScale');
/**
* If areaGradient is specified sets the areaGradient setting and returns the Line instance itself. If areaGradient is true, when a line chart shape area, each area filled gradient.
* @function
* @example
* line.areaGradient(true)
* @param {boolean} [areaGradient=false] If is true, each area filled gradient.
* @return {areaGradient|Line}
*/
Line.prototype.areaGradient = attrFunc('areaGradient');
function domainY(fieldY, munged, level=0, aggregated=false, stacked=false) {
return fieldY.munged(munged).level(level).aggregated(aggregated).domain(0, stacked);
}
export default genFunc(Line);
export {conditions, domainY, shapes};