@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
326 lines (268 loc) • 9.96 kB
JavaScript
import { drawing as draw } from '@progress/kendo-drawing';
import { BoxElement, FloatElement, TextBox } from '../../core';
import { AREA, LEGEND_ITEM_ARIA_ROLE_DESCRIPTION, LEGEND_ITEM_CLASSNAME, LEGEND_ITEM_CLICK, LEGEND_ITEM_HOVER, LEGEND_ITEM_LEAVE, LEGEND_ITEM_ROLE, LINE } from '../constants';
import { BOTTOM, CENTER, WHITE } from '../../common/constants';
import { deepExtend, eventElement, setDefaultOptions } from '../../common';
import addAccessibilityAttributesToVisual from '../../core/utils/add-accessibility-attributes-to-visual';
import guid from '../../core/utils/guid';
import LegendItemMarker from './legend-item-marker';
import MarkerLineArea from './legend-item-line-area';
import LegendItemLine from './legend-item-line';
import LegendItemSquare from './legend-item-square';
class LegendItem extends BoxElement {
constructor(options) {
super(options);
this.createContainer();
if (!options.rtl) {
this.createMarker();
this.createLabel();
} else {
this.createLabel();
this.createMarker();
}
this._id = guid();
this.options.accessibility.ariaChecked = options.active;
}
createContainer() {
this.container = new FloatElement({ vertical: false, wrap: false, align: CENTER, spacing: this.options.spacing });
this.append(this.container);
}
createMarker() {
this.markerWrap = new BoxElement({ vertical: false, shrinkToFit: true, wrap: false, margin: 1, width: 22, height: 22 });
this.container.append(this.markerWrap);
this.createMarkerArea();
if (this.options.markers.visible) {
this._marker = this._createMarker();
this.markerWrap.append(this._marker);
}
}
createMarkerArea() {
const options = this.options;
const { markerColor, line = {} } = options;
const lineOptions = {
border: {
color: line.color || markerColor,
opacity: line.opacity,
dashType: line.dashType
}
};
return this._createLine(lineOptions) ||
this._createMarkerLine(lineOptions, line) ||
this._createSquare();
}
markerOptions() {
const options = this.options;
const { markers = {}, markerColor } = options;
const { border = {} } = markers;
markers.zIndex = undefined;
return deepExtend({}, markers, {
border: { color: border.color || markerColor },
highlight: options.highlight.markers
});
}
_highlightOptions() {
const options = this.options;
return deepExtend(
{ markers: { type: options.markers.type } },
options.highlight
);
}
_createLine(lineOptions) {
const options = this.options;
if (options.type === LINE && !options.markers.visible) {
this._line = new LegendItemLine(deepExtend({}, {
background: options.markerColor,
highlight: this._highlightOptions(),
}, lineOptions, options.line));
this.markerWrap.append(this._line);
}
return this._line;
}
_createMarkerLine(lineOptions, line) {
const options = this.options;
if (options.type === LINE) {
this._markerLineArea = new MarkerLineArea(deepExtend({}, {
border: {
width: line.height
}
}, lineOptions));
this.markerWrap.append(this._markerLineArea);
}
return this._markerLineArea;
}
_reduceSize(object, prop, factor = 0.6) {
if (typeof object[prop] === 'number') {
object[prop] = object[prop] * factor;
}
}
_createSquare() {
const options = this.options;
if (options.type === AREA) {
let pattern = options.pattern || (options.series || {}).pattern;
if (pattern) {
if (typeof pattern === 'function') {
pattern = pattern(options.series);
}
pattern = structuredClone(pattern);
this._reduceSize(pattern, 'gap');
this._reduceSize(pattern, 'width');
this._reduceSize(pattern, 'radius');
}
this._square = new LegendItemSquare(Object.assign({}, {border: options.border,
vAlign: options.markers.visible ? BOTTOM : CENTER,
highlight: this._highlightOptions()},
options.area,
{pattern: pattern,
background: options.area.background || options.markerColor}));
this.markerWrap.append(this._square);
}
return this._square;
}
_createMarker() {
return new LegendItemMarker(this.markerOptions());
}
_highlightMarkers() {
if (this.options.active) {
this._toggleHighlight(true);
}
}
_restoreMarkers() {
this._toggleHighlight(false);
}
_toggleHighlight(show) {
if (!this.options.highlight.visible) {
return;
}
const element = this._marker || this._square || this._line;
if (element && element === this._line) {
this._line.visual.visible(!show);
}
if (element) {
let highlight = element.highlight;
if (!highlight) {
highlight = element.createHighlight();
highlight.forEach(h => h && this.markerWrap.appendVisual(h));
}
highlight.forEach(h => h && h.visible(show));
}
}
createLabel() {
const options = this.options;
const labelOptions = deepExtend({}, options.labels);
this.container.append(new TextBox(options.text, labelOptions));
}
getAriaLabelText() {
return this.options.text;
}
focusVisual() {
this.visual.options.set("id", this._id);
this.toggleFocusHighlight(true);
this._highlightMarkers();
}
clearFocusFromVisual() {
this.visual.options.set("id", "");
this.toggleFocusHighlight(false);
this._restoreMarkers();
}
renderComplete() {
super.renderComplete();
const cursor = this.options.cursor || {};
const eventSink = this._itemOverlay = draw.Path.fromRect(this.container.box.toRect(), {
fill: {
color: WHITE,
opacity: 0
},
stroke: null,
cursor: cursor.style || cursor
});
this.appendVisual(eventSink);
}
click(widget, e) {
const args = this.eventArgs(e);
if (!widget.trigger(LEGEND_ITEM_CLICK, args) && e && e.type === 'contextmenu') {
e.preventDefault();
}
}
over(widget, e) {
const args = this.eventArgs(e);
if (!widget.trigger(LEGEND_ITEM_HOVER, args)) {
widget._legendItemHover(args.seriesIndex, args.pointIndex);
this._highlightMarkers();
}
// Don't trigger point hover for legend items
return true;
}
out(widget, e) {
widget._unsetActivePoint();
this._restoreMarkers();
widget.trigger(LEGEND_ITEM_LEAVE, this.eventArgs(e));
}
eventArgs(e) {
const options = this.options;
return {
element: eventElement(e),
text: options.text,
series: options.series,
seriesIndex: options.series.index,
pointIndex: options.pointIndex
};
}
createVisual() {
super.createVisual();
const options = this.options;
if (this.options.visible) {
const accessibilityOptions = deepExtend({
ariaLabel: options.accessibility.ariaLabel !== undefined ? options.accessibility.ariaLabel : options.text
}, options.accessibility);
addAccessibilityAttributesToVisual(this.visual, accessibilityOptions);
}
}
renderVisual() {
const options = this.options;
const customVisual = options.visual;
if (customVisual) {
this.visual = customVisual({
active: options.active,
series: options.series,
sender: this.getSender(),
pointIndex: options.pointIndex,
options: {
type: options.type,
// Passing the markerColor as a background option for backwards compatibility.
// Example in jq docs - https://docs.telerik.com/kendo-ui/api/javascript/dataviz/ui/chart/configuration/legend.item#legenditemvisual
markers: deepExtend({ background: this.options.markerColor }, this.markerOptions()),
labels: options.labels
},
createVisual: () => {
this.createVisual();
this.renderChildren();
this.renderComplete();
const defaultVisual = this.visual;
delete this.visual;
return defaultVisual;
}
});
this._marker = this._markerLineArea = this._square = this._line = null;
this.addVisual();
} else {
super.renderVisual();
}
}
createFocusHighlight(style) {
const borderWidth = style.stroke.width;
return draw.Path.fromRect(this.container.box.pad(borderWidth / 2).toRect(), style);
}
}
setDefaultOptions(LegendItem, {
accessibility: {
role: LEGEND_ITEM_ROLE,
className: LEGEND_ITEM_CLASSNAME,
ariaRoleDescription: LEGEND_ITEM_ARIA_ROLE_DESCRIPTION
},
markers: {},
highlight: {
visible: true,
markers: {}
}
});
export default LegendItem;