UNPKG

@progress/kendo-charts

Version:

Kendo UI platform-independent Charts library

428 lines (353 loc) 11.6 kB
import { geometry as geom, Color } from '@progress/kendo-drawing'; import { ChartElement, TextBox, ShapeElement, Box, Point } from '../../core'; import PointEventsMixin from '../mixins/point-events-mixin'; import NoteMixin from '../mixins/note-mixin'; import { LINE_MARKER_SIZE, FADEIN, INITIAL_ANIMATION_DURATION, BORDER_BRIGHTNESS, TOOLTIP_OFFSET, ABOVE, BELOW, CHART_POINT_ROLE, CHART_POINT_CLASSNAME, CHART_POINT_ROLE_DESCRIPTION } from '../constants'; import { WHITE, CIRCLE, CENTER, TOP, BOTTOM, LEFT, HIGHLIGHT_ZINDEX } from '../../common/constants'; import { deepExtend, getTemplate, getAriaTemplate, valueOrDefault, getSpacing } from '../../common'; import elementId from '../../core/utils/element-id'; import unclipBox from '../utils/unclip-box'; class LinePoint extends ChartElement { constructor(value, options) { super(options); this.value = value; this.aboveAxis = valueOrDefault(this.options.aboveAxis, true); this.tooltipTracking = true; this._id = elementId(); } initOptions(options) { this.options = Object.assign({}, options); } render() { const { markers } = this.options; if (this._rendered) { return; } this._rendered = true; if (markers.visible && markers.size) { this.marker = this.createMarker(); this.append(this.marker); } 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 TextBox(this.getLabelText(options), Object.assign({}, {align: CENTER, vAlign: CENTER, zIndex: valueOrDefault(options.zIndex, this.series.zIndex)}, options, {margin: Object.assign({}, {left: 5, right: 5}, options.margin)}), this.pointData() ); } getLabelText(options) { let labelTemplate = getTemplate(options); if (labelTemplate) { return labelTemplate(this.pointData()); } else if (options.format) { return this.formatValue(options.format); } return this.value; } getAriaLabelText() { const labels = this.options.labels; const ariaTemplate = getAriaTemplate(labels); if (ariaTemplate) { return ariaTemplate(this.pointData()); } return this.getLabelText(labels); } markerBorder() { const options = this.options.markers; const background = options.background; const border = Object.assign({}, {color: this.color}, options.border); if (border.color === undefined) { border.color = new Color(background).brightness(BORDER_BRIGHTNESS).toHex(); } return border; } createVisual() { } createMarker() { const options = this.options.markers; const marker = new ShapeElement({ type: options.type, width: options.size, height: options.size, rotation: options.rotation, background: options.background, border: this.markerBorder(), opacity: options.opacity, pattern: this.options.pattern, zIndex: valueOrDefault(options.zIndex, this.series.zIndex), animation: options.animation, visual: options.visual, accessibilityOptions: Object.assign({}, {ariaLabel: this.getAriaLabelText(this.options.labels)}, this.options.accessibility) }, { dataItem: this.dataItem, value: this.value, series: this.series, category: this.category }); return marker; } markerBox() { if (!this.marker) { this.marker = this.createMarker(); this.marker.reflow(this._childBox); } return this.marker.box; } reflow(targetBox) { const { options, aboveAxis } = this; const vertical = options.vertical; this.render(); this.box = targetBox; const childBox = targetBox.clone(); if (vertical) { if (aboveAxis) { childBox.y1 -= childBox.height(); } else { childBox.y2 += childBox.height(); } } else { if (aboveAxis) { childBox.x1 += childBox.width(); } else { childBox.x2 -= childBox.width(); } } this._childBox = childBox; if (this.marker) { this.marker.reflow(childBox); } this.reflowLabel(childBox); if (this.errorBars) { for (let i = 0; i < this.errorBars.length; i++) { this.errorBars[i].reflow(childBox); } } if (this.note) { let noteTargetBox = this.markerBox(); if (!(options.markers.visible && options.markers.size)) { const center = noteTargetBox.center(); noteTargetBox = new Box(center.x, center.y, center.x, center.y); } this.note.reflow(noteTargetBox); } } reflowLabel(box) { const { options, label } = this; let anchor = options.labels.position; if (label) { anchor = anchor === ABOVE ? TOP : anchor; anchor = anchor === BELOW ? BOTTOM : anchor; label.reflow(box); label.box.alignTo(this.markerBox(), anchor); label.reflow(label.box); } } createHighlight() { const markers = this.options.highlight.markers; const defaultColor = this.markerBorder().color; const options = this.options.markers; const size = options.size + (options.border.width || 0) + (markers.border.width || 0); const shadow = new ShapeElement({ type: options.type, width: size, height: size, rotation: options.rotation, background: markers.color || defaultColor, border: { color: markers.border.color, width: markers.border.width, opacity: valueOrDefault(markers.border.opacity, 1) }, opacity: valueOrDefault(markers.opacity, 1) }); shadow.reflow(this._childBox); return shadow.getElement(); } highlightVisual() { return (this.marker || {}).visual; } highlightVisualArgs() { const marker = this.marker; let visual, rect; if (marker) { rect = marker.paddingBox.toRect(); visual = marker.visual; } else { const size = this.options.markers.size; const halfSize = size / 2; const center = this.box.center(); rect = new geom.Rect([center.x - halfSize, center.y - halfSize], [size, size]); } return { options: this.options, rect: rect, visual: visual }; } createFocusHighlight() { const markerOptions = this.options.markers; const highlightOptions = this.options.focusHighlight; const size = markerOptions.size + (markerOptions.border.width || 0); const highlight = new ShapeElement({ type: markerOptions.type, width: size, height: size, rotation: markerOptions.rotation, background: highlightOptions.color, border: highlightOptions.border, opacity: highlightOptions.opacity, padding: highlightOptions.border.width / 2, zIndex: highlightOptions.zIndex }); highlight.reflow(this._childBox); return highlight.getElement(); } tooltipAnchor() { const markerBox = this.markerBox(); const clipBox = this.owner.pane.clipBox(); const showTooltip = !clipBox || clipBox.overlaps(markerBox); if (showTooltip) { const x = markerBox.x2 + TOOLTIP_OFFSET; const horizontalAlign = LEFT; let y, verticalAlign; if (this.aboveAxis) { y = markerBox.y1; verticalAlign = BOTTOM; } else { y = markerBox.y2; verticalAlign = TOP; } return { point: new Point(x, y), align: { horizontal: horizontalAlign, vertical: verticalAlign } }; } } formatValue(format) { return this.owner.formatPointValue(this, format); } overlapsBox(box) { const markerBox = this.markerBox(); return markerBox.overlaps(box); } clipElements() { this.options.visible = false; } unclipElements() { if (this.label) { this.label.options.noclip = true; } if (this.note) { this.note.options.noclip = true; } } unclipBox() { return unclipBox(this.markerBox().clone(), [this.label, this.note]); } labelBox() { return this.label ? this.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, series: this.series }; } focusVisual() { if (this.marker) { if (this.marker.visual) { this.marker.visual.options.set("id", this._id); } this.toggleFocusHighlight(true); } } clearFocusFromVisual() { if (this.marker) { if (this.marker.visual) { this.marker.visual.options.set("id", ""); } this.toggleFocusHighlight(false); } } getIndex() { return this.categoryIx !== undefined ? this.categoryIx : this.pointIx; } } LinePoint.prototype.defaults = { vertical: true, markers: { visible: true, background: WHITE, size: LINE_MARKER_SIZE, type: CIRCLE, border: { width: 2 }, opacity: 1 }, labels: { visible: false, position: ABOVE, margin: getSpacing(3), padding: getSpacing(4), animation: { type: FADEIN, delay: INITIAL_ANIMATION_DURATION } }, notes: { label: {} }, highlight: { markers: { border: { color: "#fff", width: 2 } }, zIndex: HIGHLIGHT_ZINDEX }, errorBars: { line: { width: 1 } }, accessibility: { tabIndex: 0, role: CHART_POINT_ROLE, className: CHART_POINT_CLASSNAME, ariaRoleDescription: CHART_POINT_ROLE_DESCRIPTION } }; deepExtend(LinePoint.prototype, PointEventsMixin); deepExtend(LinePoint.prototype, NoteMixin); export default LinePoint;