UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

244 lines (206 loc) 6.84 kB
import { Color } from '@progress/kendo-drawing'; import Candlestick from '../candlestick-chart/candlestick'; import PointEventsMixin from '../mixins/point-events-mixin'; import { ShapeElement } from '../../core'; import { LINE_MARKER_SIZE, BORDER_BRIGHTNESS } from '../constants'; import { CROSS, CIRCLE, WHITE } from '../../common/constants'; import { deepExtend, defined, setDefaultOptions } from '../../common'; class BoxPlot extends Candlestick { constructor(value, options) { super(value, options); this.createNote(); } reflow(box) { const { options, value, owner: chart } = this; const valueAxis = chart.seriesValueAxis(options); let whiskerSlot, boxSlot; this.boxSlot = boxSlot = valueAxis.getSlot(value.q1, value.q3); this.realBody = boxSlot; this.reflowBoxSlot(box); this.whiskerSlot = whiskerSlot = valueAxis.getSlot(value.lower, value.upper); this.reflowWhiskerSlot(box); const medianSlot = valueAxis.getSlot(value.median); if (value.mean) { const meanSlot = valueAxis.getSlot(value.mean); this.meanPoints = this.calcMeanPoints(box, meanSlot); } this.whiskerPoints = this.calcWhiskerPoints(boxSlot, whiskerSlot); this.medianPoints = this.calcMedianPoints(box, medianSlot); this.box = whiskerSlot.clone().wrap(boxSlot); this.reflowNote(); } reflowBoxSlot(box) { this.boxSlot.x1 = box.x1; this.boxSlot.x2 = box.x2; } reflowWhiskerSlot(box) { this.whiskerSlot.x1 = box.x1; this.whiskerSlot.x2 = box.x2; } calcMeanPoints(box, meanSlot) { return [ [ [ box.x1, meanSlot.y1 ], [ box.x2, meanSlot.y1 ] ] ]; } calcWhiskerPoints(boxSlot, whiskerSlot) { const mid = whiskerSlot.center().x; return [ [ [ mid - 5, whiskerSlot.y1 ], [ mid + 5, whiskerSlot.y1 ], [ mid, whiskerSlot.y1 ], [ mid, boxSlot.y1 ] ], [ [ mid - 5, whiskerSlot.y2 ], [ mid + 5, whiskerSlot.y2 ], [ mid, whiskerSlot.y2 ], [ mid, boxSlot.y2 ] ] ]; } calcMedianPoints(box, medianSlot) { return [ [ [ box.x1, medianSlot.y1 ], [ box.x2, medianSlot.y1 ] ] ]; } renderOutliers(options) { const value = this.value; const outliers = value.outliers || []; const outerFence = Math.abs(value.q3 - value.q1) * 3; const elements = []; let markers = options.markers || {}; for (let i = 0; i < outliers.length; i++) { const outlierValue = outliers[i]; if (outlierValue < value.q3 + outerFence && outlierValue > value.q1 - outerFence) { markers = options.outliers; } else { markers = options.extremes; } let markersBorder = deepExtend({}, markers.border); if (!defined(markersBorder.color)) { if (defined(this.color)) { markersBorder.color = this.color; } else { markersBorder.color = new Color(markers.background).brightness(BORDER_BRIGHTNESS).toHex(); } } const shape = new ShapeElement({ type: markers.type, width: markers.size, height: markers.size, rotation: markers.rotation, background: markers.background, border: markersBorder, opacity: markers.opacity }); shape.value = outlierValue; elements.push(shape); } this.reflowOutliers(elements); return elements; } reflowOutliers(outliers) { const valueAxis = this.owner.seriesValueAxis(this.options); const center = this.box.center(); for (let i = 0; i < outliers.length; i++) { const outlierValue = outliers[i].value; const markerBox = valueAxis.getSlot(outlierValue); if (this.options.vertical) { markerBox.move(center.x); } else { markerBox.move(undefined, center.y); } this.box = this.box.wrap(markerBox); outliers[i].reflow(markerBox); } } mainVisual(options) { const group = super.mainVisual(options); const outliers = this.renderOutliers(options); for (let i = 0; i < outliers.length; i++) { const element = outliers[i].getElement(); if (element) { group.append(element); } } return group; } createLines(container, options) { this.drawLines(container, options, this.whiskerPoints, options.whiskers); this.drawLines(container, options, this.medianPoints, options.median); this.drawLines(container, options, this.meanPoints, options.mean); } getBorderColor() { if ((this.options.border || {}).color) { return this.options.border.color; } if (this.color) { return this.color; } return super.getBorderColor(); } } setDefaultOptions(BoxPlot, { border: { _brightness: 0.8 }, line: { width: 2 }, median: { color: "#f6f6f6" }, mean: { width: 2, dashType: "dash", color: "#f6f6f6" }, overlay: { gradient: "glass" }, tooltip: { format: "<table>" + "<tr><th colspan='2'>{6:d}</th></tr>" + "<tr><td>Lower:</td><td>{0:C}</td></tr>" + "<tr><td>Q1:</td><td>{1:C}</td></tr>" + "<tr><td>Median:</td><td>{2:C}</td></tr>" + "<tr><td>Mean:</td><td>{5:C}</td></tr>" + "<tr><td>Q3:</td><td>{3:C}</td></tr>" + "<tr><td>Upper:</td><td>{4:C}</td></tr>" + "</table>" }, highlight: { opacity: 1, border: { width: 1, opacity: 1 }, line: { width: 1, opacity: 1 } }, notes: { visible: true, label: {} }, outliers: { visible: true, size: LINE_MARKER_SIZE, type: CROSS, background: WHITE, border: { width: 2, opacity: 1 }, opacity: 0 }, extremes: { visible: true, size: LINE_MARKER_SIZE, type: CIRCLE, background: WHITE, border: { width: 2, opacity: 1 }, opacity: 0 } }); deepExtend(BoxPlot.prototype, PointEventsMixin); export default BoxPlot;