UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

335 lines (272 loc) 9.24 kB
import { drawing as draw, Color } from '@progress/kendo-drawing'; import BarLabel from './bar-label'; import { CHART_POINT_ROLE_DESCRIPTION, CHART_POINT_CLASSNAME, CHART_POINT_ROLE, BORDER_BRIGHTNESS, TOOLTIP_OFFSET } from '../constants'; import hasGradientOverlay from '../utils/has-gradient-overlay'; import { ChartElement, createPatternFill, Point, Box } from '../../core'; import PointEventsMixin from '../mixins/point-events-mixin'; import NoteMixin from '../mixins/note-mixin'; import AccessibilityAttributesMixin from '../mixins/accessibility-attributes-mixin'; import { WHITE, LEFT, RIGHT, BOTTOM, TOP } from '../../common/constants'; import { alignPathToPixel, deepExtend, defined, getTemplate, valueOrDefault } from '../../common'; import unclipBox from '../utils/unclip-box'; const BAR_ALIGN_MIN_WIDTH = 6; class Bar extends ChartElement { constructor(value, options) { super(); this.options = options; this.color = options.color || WHITE; this.aboveAxis = valueOrDefault(this.options.aboveAxis, true); this.value = value; } render() { if (this._rendered) { return; } this._rendered = true; this.createLabel(); this.createNote(); if (this.errorBar) { this.append(this.errorBar); } } createLabel() { const options = this.options; const labels = options.labels; if (labels.visible) { this.label = this.createLabelElement(labels); this.append(this.label); } } createLabelElement(options) { return new BarLabel(this.getLabelText(options), deepExtend({ vertical: this.options.vertical }, options ), this.pointData()); } getLabelText(options) { let labelTemplate = getTemplate(options); if (labelTemplate) { return labelTemplate(this.pointData()); } return this.formatValue(options.format); } formatValue(format) { return this.owner.formatPointValue(this, format); } reflow(targetBox) { this.render(); const label = this.label; this.box = targetBox; if (label) { label.options.aboveAxis = this.aboveAxis; label.reflow(targetBox); } if (this.note) { this.note.reflow(targetBox); } if (this.errorBars) { for (let i = 0; i < this.errorBars.length; i++) { this.errorBars[i].reflow(targetBox); } } } createVisual() { const { box, options } = this; const customVisual = options.visual; if (this.visible !== false) { super.createVisual(); this.addAccessibilityAttributesToVisual(); if (customVisual) { const visual = this.rectVisual = customVisual({ category: this.category, dataItem: this.dataItem, value: this.value, sender: this.getSender(), series: this.series, percentage: this.percentage, stackValue: this.stackValue, runningTotal: this.runningTotal, total: this.total, rect: box.toRect(), createVisual: () => { const group = new draw.Group(); this.createRect(group); return group; }, options: options }); if (visual) { this.visual.append(visual); } } else if (box.width() > 0 && box.height() > 0) { this.createRect(this.visual); } } } createRect(visual) { const options = this.options; const border = options.border; const strokeOpacity = defined(border.opacity) ? border.opacity : options.opacity; const rect = this.box.toRect(); rect.size.width = Math.round(rect.size.width); const path = this.rectVisual = draw.Path.fromRect(rect, { fill: createPatternFill(options.pattern, { color: this.color, opacity: options.opacity }), stroke: { color: this.getBorderColor(), width: border.width, opacity: strokeOpacity, dashType: border.dashType } }); const width = this.box.width(); const height = this.box.height(); const size = options.vertical ? width : height; if (size > BAR_ALIGN_MIN_WIDTH) { alignPathToPixel(path); // Fixes lineJoin issue in firefox when the joined lines are parallel if (width < 1 || height < 1) { path.options.stroke.lineJoin = "round"; } } visual.append(path); if (hasGradientOverlay(options)) { const overlay = this.createGradientOverlay(path, { baseColor: this.color }, deepExtend({ end: !options.vertical ? [ 0, 1 ] : undefined }, options.overlay)); visual.append(overlay); } } createHighlight(style) { const highlight = draw.Path.fromRect(this.box.toRect(), style); return alignPathToPixel(highlight); } highlightVisual() { return this.rectVisual; } highlightVisualArgs() { return { options: this.options, rect: this.box.toRect(), visual: this.rectVisual }; } createFocusHighlight(style) { const borderWidth = this.options.focusHighlight.border.width; const highlight = draw.Path.fromRect(this.box.pad(borderWidth / 2).toRect(), style); return alignPathToPixel(highlight); } getBorderColor() { const color = this.color; const border = this.options.border; const brightness = border._brightness || BORDER_BRIGHTNESS; let borderColor = border.color; if (!defined(borderColor)) { borderColor = new Color(color).brightness(brightness).toHex(); } return borderColor; } tooltipAnchor() { const { options, box, aboveAxis } = this; const clipBox = this.owner.pane.clipBox() || box; let horizontalAlign = LEFT; let verticalAlign = TOP; let x, y; if (options.vertical) { x = Math.min(box.x2, clipBox.x2) + TOOLTIP_OFFSET; if (aboveAxis) { y = Math.max(box.y1, clipBox.y1); } else { y = Math.min(box.y2, clipBox.y2); verticalAlign = BOTTOM; } } else { const x1 = Math.max(box.x1, clipBox.x1); const x2 = Math.min(box.x2, clipBox.x2); if (options.isStacked) { verticalAlign = BOTTOM; if (aboveAxis) { horizontalAlign = RIGHT; x = x2; } else { x = x1; } y = Math.max(box.y1, clipBox.y1) - TOOLTIP_OFFSET; } else { if (aboveAxis) { x = x2 + TOOLTIP_OFFSET; } else { x = x1 - TOOLTIP_OFFSET; horizontalAlign = RIGHT; } y = Math.max(box.y1, clipBox.y1); } } return { point: new Point(x, y), align: { horizontal: horizontalAlign, vertical: verticalAlign } }; } overlapsBox(box) { return this.box.overlaps(box); } unclipBox() { const label = this.label && this.label.textBox; return unclipBox(this.box.clone(), [label, this.note]); } labelBox() { const label = this.label && this.label.textBox; return label ? label.box : new Box(); } noteBox() { return this.note ? this.note.box : new Box(); } pointData() { return { dataItem: this.dataItem, category: this.category, value: this.value, percentage: this.percentage, stackValue: this.stackValue, runningTotal: this.runningTotal, total: this.total, series: this.series }; } getIndex() { return this.categoryIx; } } deepExtend(Bar.prototype, PointEventsMixin); deepExtend(Bar.prototype, NoteMixin); deepExtend(Bar.prototype, AccessibilityAttributesMixin); Bar.prototype.defaults = { border: { width: 1 }, vertical: true, overlay: { gradient: "glass" }, labels: { visible: false, format: "{0}" }, opacity: 1, notes: { label: {} }, accessibility: { role: CHART_POINT_ROLE, className: CHART_POINT_CLASSNAME, ariaRoleDescription: CHART_POINT_ROLE_DESCRIPTION } }; export default Bar;