@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
428 lines (353 loc) • 11.6 kB
JavaScript
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;