@visactor/vchart
Version:
charts lib based @visactor/VGrammar
310 lines (277 loc) • 14.9 kB
JavaScript
import { AttributeLevel } from "../../constant/attribute";
import { DEFAULT_DATA_INDEX } from "../../constant/data";
import { PREFIX } from "../../constant/base";
import { valueInScaleRange } from "../../util/scale";
import { CartesianSeries } from "../cartesian/cartesian";
import { SeriesTypeEnum } from "../interface/type";
import { STATE_VALUE_ENUM } from "../../compile/mark/interface";
import { registerDataSetInstanceTransform } from "../../data/register";
import { DataView } from "@visactor/vdataset";
import { foldOutlierData } from "../../data/transforms/box-plot";
import { BOX_PLOT_OUTLIER_VALUE_FIELD } from "../../constant/box-plot";
import { BoxPlotSeriesTooltipHelper } from "./tooltip-helper";
import { addVChartProperty } from "../../data/transforms/add-property";
import { addDataKey, initKeyMap } from "../../data/transforms/data-key";
import { animationConfig, userAnimationConfig } from "../../animation/utils";
import { registerScaleInOutAnimation } from "../../animation/config";
import { registerBoxPlotMark } from "../../mark/box-plot";
import { registerSymbolMark } from "../../mark/symbol";
import { boxPlotSeriesMark } from "./constant";
import { Factory } from "../../core/factory";
import { merge, isNumber } from "@visactor/vutils";
import { getGroupAnimationParams } from "../util/utils";
import { registerCartesianLinearAxis, registerCartesianBandAxis } from "../../component/axis/cartesian";
import { CompilableData } from "../../compile/data";
import { registeBoxPlotScaleAnimation } from "./animation";
import { boxPlot } from "../../theme/builtin/common/series/box-plot";
const DEFAULT_STROKE_WIDTH = 2, DEFAULT_SHAFT_FILL_OPACITY = .5, DEFAULT_SHAFT_SHAPE = "line";
export const DEFAULT_FILL_COLOR = "#FFF";
export const DEFAULT_STROKE_COLOR = "#000";
const DEFAULT_OUTLIER_SIZE = 10;
export class BoxPlotSeries extends CartesianSeries {
constructor() {
super(...arguments), this.type = SeriesTypeEnum.boxPlot;
}
getMinField() {
return this._minField;
}
getMaxField() {
return this._maxField;
}
getQ1Field() {
return this._q1Field;
}
getMedianField() {
return this._medianField;
}
getQ3Field() {
return this._q3Field;
}
getOutliersField() {
return this._outliersField;
}
getShaftShape() {
return this._shaftShape;
}
getBoxFillColor() {
return this._boxFillColor;
}
getStrokeColor() {
return this._strokeColor;
}
getOutliersStyle() {
return this._outliersStyle;
}
setAttrFromSpec() {
var _a, _b, _c, _d, _e;
super.setAttrFromSpec();
const boxPlotStyle = null !== (_b = null === (_a = this._spec.boxPlot) || void 0 === _a ? void 0 : _a.style) && void 0 !== _b ? _b : {};
this._minField = this._spec.minField, this._maxField = this._spec.maxField, this._q1Field = this._spec.q1Field,
this._medianField = this._spec.medianField, this._q3Field = this._spec.q3Field,
this._outliersField = this._spec.outliersField, this._lineWidth = null !== (_c = boxPlotStyle.lineWidth) && void 0 !== _c ? _c : 2,
this._boxWidth = boxPlotStyle.boxWidth, this._shaftShape = null !== (_d = boxPlotStyle.shaftShape) && void 0 !== _d ? _d : "line",
this._shaftWidth = boxPlotStyle.shaftWidth, this._boxFillColor = boxPlotStyle.boxFill,
this._strokeColor = boxPlotStyle.stroke, this._shaftFillOpacity = "bar" === this._shaftShape ? null !== (_e = boxPlotStyle.shaftFillOpacity) && void 0 !== _e ? _e : .5 : void 0,
this._outliersStyle = this._spec.outliersStyle;
}
initMark() {
this._boxPlotMark = this._createMark(BoxPlotSeries.mark.boxPlot, {
groupKey: this._seriesField,
isSeriesMark: !0
}), this._outliersField && (this._outlierMark = this._createMark(BoxPlotSeries.mark.outlier, {
key: DEFAULT_DATA_INDEX,
groupKey: this._seriesField,
dataView: this._outlierDataView.getDataView(),
dataProductId: this._outlierDataView.getProductId()
}));
}
initMarkStyle() {
var _a, _b, _c, _d, _e;
const boxPlotMark = this._boxPlotMark;
if (boxPlotMark) {
const commonBoxplotStyles = {
lineWidth: this._lineWidth,
fill: null !== (_a = this._boxFillColor) && void 0 !== _a ? _a : "line" === this._shaftShape ? "#FFF" : this.getColorAttribute(),
minMaxFillOpacity: this._shaftFillOpacity,
stroke: null !== (_b = this._strokeColor) && void 0 !== _b ? _b : "line" === this._shaftShape ? this.getColorAttribute() : "#000"
};
boxPlotMark.setGlyphConfig({
direction: this._direction,
shaftShape: this._shaftShape
});
const boxPlotMarkStyles = "horizontal" === this._direction ? Object.assign(Object.assign({
y: this.dataToPositionY.bind(this)
}, commonBoxplotStyles), {
boxHeight: () => {
var _a;
return null !== (_a = this._boxWidth) && void 0 !== _a ? _a : this._getMarkWidth();
},
ruleHeight: () => {
var _a;
return null !== (_a = this._shaftWidth) && void 0 !== _a ? _a : this._getMarkWidth();
},
q1q3Height: () => {
var _a;
return null !== (_a = this._boxWidth) && void 0 !== _a ? _a : this._getMarkWidth();
},
minMaxHeight: () => {
var _a;
return null !== (_a = this._shaftWidth) && void 0 !== _a ? _a : this._getMarkWidth();
}
}) : Object.assign(Object.assign({
x: this.dataToPositionX.bind(this)
}, commonBoxplotStyles), {
boxWidth: () => {
var _a;
return null !== (_a = this._boxWidth) && void 0 !== _a ? _a : this._getMarkWidth();
},
ruleWidth: () => {
var _a;
return null !== (_a = this._shaftWidth) && void 0 !== _a ? _a : this._getMarkWidth();
},
q1q3Width: () => {
var _a;
return null !== (_a = this._boxWidth) && void 0 !== _a ? _a : this._getMarkWidth();
},
minMaxWidth: () => {
var _a;
return null !== (_a = this._shaftWidth) && void 0 !== _a ? _a : this._getMarkWidth();
}
});
this.setMarkStyle(boxPlotMark, boxPlotMarkStyles, STATE_VALUE_ENUM.STATE_NORMAL, AttributeLevel.Series);
}
const outlierMark = this._outlierMark;
outlierMark && this.setMarkStyle(outlierMark, {
fill: null !== (_d = null === (_c = this._outliersStyle) || void 0 === _c ? void 0 : _c.fill) && void 0 !== _d ? _d : this.getColorAttribute(),
size: isNumber(null === (_e = this._outliersStyle) || void 0 === _e ? void 0 : _e.size) ? this._outliersStyle.size : 10,
symbolType: "circle"
}, STATE_VALUE_ENUM.STATE_NORMAL, AttributeLevel.Series);
}
initBoxPlotMarkStyle() {
var _a, _b;
const boxPlotMark = this._boxPlotMark, axisHelper = "horizontal" === this._direction ? this._xAxisHelper : this._yAxisHelper;
if (boxPlotMark && axisHelper) {
const {dataToPosition: dataToPosition} = axisHelper, scale = null === (_a = null == axisHelper ? void 0 : axisHelper.getScale) || void 0 === _a ? void 0 : _a.call(axisHelper, 0);
this.setMarkStyle(boxPlotMark, {
min: datum => valueInScaleRange(dataToPosition(this.getDatumPositionValues(datum, this._minField), {
bandPosition: this._bandPosition
}), scale),
q1: datum => valueInScaleRange(dataToPosition(this.getDatumPositionValues(datum, this._q1Field), {
bandPosition: this._bandPosition
}), scale),
median: datum => valueInScaleRange(dataToPosition(this.getDatumPositionValues(datum, this._medianField), {
bandPosition: this._bandPosition
}), scale),
q3: datum => valueInScaleRange(dataToPosition(this.getDatumPositionValues(datum, this._q3Field), {
bandPosition: this._bandPosition
}), scale),
max: datum => valueInScaleRange(dataToPosition(this.getDatumPositionValues(datum, this._maxField), {
bandPosition: this._bandPosition
}), scale)
}, STATE_VALUE_ENUM.STATE_NORMAL, AttributeLevel.Series);
}
const outlierMark = this._outlierMark;
if (outlierMark && axisHelper) {
const {dataToPosition: dataToPosition} = axisHelper, scale = null === (_b = null == axisHelper ? void 0 : axisHelper.getScale) || void 0 === _b ? void 0 : _b.call(axisHelper, 0), outlierMarkPositionChannel = "horizontal" === this._direction ? {
y: this.dataToPositionY.bind(this),
x: datum => valueInScaleRange(dataToPosition(this.getDatumPositionValues(datum, BOX_PLOT_OUTLIER_VALUE_FIELD), {
bandPosition: this._bandPosition
}), scale)
} : {
x: this.dataToPositionX.bind(this),
y: datum => valueInScaleRange(dataToPosition(this.getDatumPositionValues(datum, BOX_PLOT_OUTLIER_VALUE_FIELD), {
bandPosition: this._bandPosition
}), scale)
};
this.setMarkStyle(outlierMark, outlierMarkPositionChannel, STATE_VALUE_ENUM.STATE_NORMAL, AttributeLevel.Series);
}
}
initData() {
if (super.initData(), !this._data) return;
registerDataSetInstanceTransform(this._dataSet, "foldOutlierData", foldOutlierData),
registerDataSetInstanceTransform(this._dataSet, "addVChartProperty", addVChartProperty);
const outlierDataView = new DataView(this._dataSet, {
name: `${this.type}_outlier_${this.id}_data`
});
outlierDataView.parse([ this.getViewData() ], {
type: "dataview"
}), outlierDataView.name = `${PREFIX}_series_${this.id}_outlierData`, outlierDataView.transform({
type: "foldOutlierData",
options: {
dimensionField: "horizontal" === this._direction ? this._fieldY : this._fieldX,
outliersField: this._outliersField
}
}), outlierDataView.transform({
type: "addVChartProperty",
options: {
beforeCall: initKeyMap.bind(this),
call: addDataKey
}
}, !1), this._outlierDataView = new CompilableData(this._option, outlierDataView);
}
compileData() {
var _a;
super.compileData(), null === (_a = this._outlierDataView) || void 0 === _a || _a.compile();
}
init(option) {
super.init(option), this.initBoxPlotMarkStyle();
}
_getMarkWidth() {
if (this._autoBoxWidth) return this._autoBoxWidth;
const bandAxisHelper = "horizontal" === this._direction ? this._yAxisHelper : this._xAxisHelper, xField = "horizontal" === this._direction ? this._fieldY : this._fieldX, autoBoxWidth = bandAxisHelper.getBandwidth(xField.length - 1) / xField.length;
return this._autoBoxWidth = autoBoxWidth, this._autoBoxWidth;
}
onLayoutEnd() {
super.onLayoutEnd(), this._autoBoxWidth = null;
}
_initAnimationSpec(config = {}) {
const newConfig = merge({}, config);
return [ "appear", "enter", "update", "exit", "disappear" ].forEach((state => {
newConfig[state] && "scaleIn" === newConfig[state].type ? newConfig[state].type = "line" === this._shaftShape ? "boxplotScaleIn" : "barBoxplotScaleIn" : newConfig[state] && "scaleOut" === newConfig[state].type && (newConfig[state].type = "line" === this._shaftShape ? "boxplotScaleOut" : "barBoxplotScaleOut");
})), newConfig;
}
initAnimation() {
var _a, _b, _c, _d, _e, _f, _g;
const animationParams = getGroupAnimationParams(this);
if (this._boxPlotMark) {
const newDefaultConfig = this._initAnimationSpec(null === (_a = Factory.getAnimationInKey("scaleInOut")) || void 0 === _a ? void 0 : _a()), newConfig = this._initAnimationSpec(userAnimationConfig("boxPlot", this._spec, this._markAttributeContext));
this._boxPlotMark.setAnimationConfig(animationConfig(newDefaultConfig, newConfig, animationParams));
}
if (this._outlierMark) {
const outlierMarkUserAnimation = {
appear: null === (_b = this._spec.animationAppear) || void 0 === _b ? void 0 : _b.symbol,
disappear: null === (_c = this._spec.animationDisappear) || void 0 === _c ? void 0 : _c.symbol,
enter: null === (_d = this._spec.animationEnter) || void 0 === _d ? void 0 : _d.symbol,
exit: null === (_e = this._spec.animationExit) || void 0 === _e ? void 0 : _e.symbol,
update: null === (_f = this._spec.animationUpdate) || void 0 === _f ? void 0 : _f.symbol
};
this._outlierMark.setAnimationConfig(animationConfig(null === (_g = Factory.getAnimationInKey("scaleInOut")) || void 0 === _g ? void 0 : _g(), outlierMarkUserAnimation, animationParams));
}
}
initTooltip() {
this._tooltipHelper = new BoxPlotSeriesTooltipHelper(this), this._boxPlotMark && this._tooltipHelper.activeTriggerSet.mark.add(this._boxPlotMark),
this._outlierMark && this._tooltipHelper.activeTriggerSet.mark.add(this._outlierMark);
}
getStatisticFields() {
const fields = super.getStatisticFields(), outliersField = fields.find((f => f.key === this._outliersField));
return outliersField && (outliersField.operations = [ "array-min", "array-max" ]),
fields;
}
onEvaluateEnd(ctx) {
super.onEvaluateEnd(ctx), this._outlierDataView.updateData();
}
getDefaultShapeType() {
return "square";
}
getActiveMarks() {
return [ this._boxPlotMark ];
}
}
BoxPlotSeries.type = SeriesTypeEnum.boxPlot, BoxPlotSeries.builtInTheme = {
boxPlot: boxPlot
}, BoxPlotSeries.mark = boxPlotSeriesMark;
export const registerBoxplotSeries = () => {
registerBoxPlotMark(), registerSymbolMark(), registerScaleInOutAnimation(), registerCartesianBandAxis(),
registerCartesianLinearAxis(), registeBoxPlotScaleAnimation(), Factory.registerSeries(BoxPlotSeries.type, BoxPlotSeries);
};
//# sourceMappingURL=box-plot.js.map