@progress/kendo-charts
Version:
Kendo UI platform-independent Charts library
314 lines (259 loc) • 9.03 kB
JavaScript
import { ChartElement, BoxElement, Box, TextBox, FloatElement } from '../../core';
import LegendLayout from './legend-layout';
import LegendItem from './legend-item';
import { TOP, RIGHT, BOTTOM, LEFT, CENTER, X, Y, BLACK, CIRCLE, POINTER, START, END, HORIZONTAL, SQUARE } from '../../common/constants';
import { deepExtend, defined, getTemplate, getSpacing, inArray, setDefaultOptions } from '../../common';
const CUSTOM = "custom";
class Legend extends ChartElement {
constructor(options, chartService = {}) {
super(options);
this.chartService = chartService;
if (!inArray(this.options.position, [ TOP, RIGHT, BOTTOM, LEFT, CUSTOM ])) {
this.options.position = RIGHT;
}
this.createContainers();
this.createLegendTitle(options.title);
this.createItems();
}
createContainers() {
const options = this.options;
const { position, align: userAlign } = options;
let align = position;
let vAlign = CENTER;
if (position === CUSTOM) {
align = LEFT;
} else if (inArray(position, [ TOP, BOTTOM ])) {
if (userAlign === START) {
align = LEFT;
} else if (userAlign === END) {
align = RIGHT;
} else {
align = CENTER;
}
vAlign = position;
} else if (userAlign) {
if (userAlign === START) {
vAlign = TOP;
} else if (userAlign === END) {
vAlign = BOTTOM;
}
}
this.container = new BoxElement({
margin: options.margin,
padding: options.padding,
background: options.background,
border: options.border,
vAlign: vAlign,
align: align,
zIndex: options.zIndex,
shrinkToFit: true
});
if (this.hasTitle()) {
this.itemsContainer = new BoxElement({
vAlign: vAlign,
align: align,
zIndex: options.zIndex,
shrinkToFit: true
});
} else {
this.itemsContainer = this.container;
}
this.append(this.container);
}
createItems() {
const chartService = this.getService();
const options = this.options;
const vertical = this.isVertical();
const innerElement = new LegendLayout({
vertical: vertical,
spacing: options.spacing,
rtl: chartService.rtl
}, chartService);
let data = options.data;
if (options.reverse) {
data = data.slice(0).reverse();
}
const count = data.length;
for (let i = 0; i < count; i++) {
let dataItem = data[i];
const { markers = {}, dashType, legendItem, opacity } = dataItem.series || {};
const markersOptions = deepExtend({ visible: markers.visible !== false, type: CIRCLE }, markers);
delete markersOptions.size;
const itemOptions = deepExtend({},
{
markers: markersOptions,
labels: options.labels,
rtl: chartService.rtl,
line: Object.assign({}, {dashType: dashType},
options.line),
area: Object.assign({}, {opacity: opacity},
options.area),
opacity: opacity,
accessibility: options.accessibility,
focusHighlight: options.focusHighlight
},
options.item,
legendItem,
dataItem,
{ markers: options.markers }
);
innerElement.append(new LegendItem(itemOptions));
}
innerElement.render();
this.itemsContainer.append(innerElement);
}
isVertical() {
const { orientation, position } = this.options;
const vertical = (position === CUSTOM && orientation !== HORIZONTAL) ||
(defined(orientation) ? orientation !== HORIZONTAL : inArray(position, [ LEFT, RIGHT ]));
return vertical;
}
hasItems() {
return this.container.children[0].children.length > 0;
}
getItems() {
return this.itemsContainer.children[0].children;
}
reflow(targetBox) {
const options = this.options;
const legendBox = targetBox.clone();
if (!this.hasItems()) {
this.box = legendBox;
return;
}
if (options.position === CUSTOM) {
this.containerCustomReflow(legendBox);
this.box = legendBox;
} else {
this.containerReflow(legendBox);
}
if (this.hasTitle()) {
this.title.reflow(new Box(this.container.box.x1, this.title.box.y1, this.container.box.x2, this.title.box.y2));
}
}
containerReflow(targetBox) {
const { options, container } = this;
const { position, width, height } = options;
const pos = position === TOP || position === BOTTOM ? X : Y;
const vertical = this.isVertical();
const alignTarget = targetBox.clone();
let containerBox = targetBox.clone();
if (position === LEFT || position === RIGHT) {
containerBox.y1 = alignTarget.y1 = 0;
}
if (vertical && height) {
containerBox.y2 = containerBox.y1 + height;
containerBox.align(alignTarget, Y, container.options.vAlign);
} else if (!vertical && width) {
containerBox.x2 = containerBox.x1 + width;
containerBox.align(alignTarget, X, container.options.align);
}
container.reflow(containerBox);
containerBox = container.box;
const box = containerBox.clone();
if (options.offsetX || options.offsetY) {
containerBox.translate(options.offsetX, options.offsetY);
container.reflow(containerBox);
}
box[pos + 1] = targetBox[pos + 1];
box[pos + 2] = targetBox[pos + 2];
this.box = box;
}
containerCustomReflow(targetBox) {
const { options, container } = this;
const { offsetX, offsetY, width, height } = options;
const vertical = this.isVertical();
let containerBox = targetBox.clone();
if (vertical && height) {
containerBox.y2 = containerBox.y1 + height;
} else if (!vertical && width) {
containerBox.x2 = containerBox.x1 + width;
}
container.reflow(containerBox);
containerBox = container.box;
container.reflow(new Box(
offsetX, offsetY,
offsetX + containerBox.width(), offsetY + containerBox.height()
));
}
renderVisual() {
if (this.hasItems()) {
super.renderVisual();
}
}
createLegendTitle(title) {
let titleOptions = deepExtend({}, {
color: BLACK,
position: TOP,
align: CENTER
}, title);
let text = titleOptions.text;
if (!title || title.visible === false || !title.text) {
return;
}
if (defined(titleOptions) && titleOptions.visible) {
const labelTemplate = getTemplate(titleOptions);
if (labelTemplate) {
text = labelTemplate({ text: text });
} else if (titleOptions.format) {
text = this.chartService.format.auto(titleOptions.format, text);
}
}
this.title = new TextBox(text, titleOptions);
this.createTitleLayout();
this.appendTitleLayoutContent();
}
createTitleLayout() {
this.layout = new FloatElement({
vertical: true,
wrap: false
});
this.container.append(this.layout);
}
hasTitle() {
return Boolean(this.options.title && this.options.title.visible !== false && this.options.title.text);
}
appendTitleLayoutContent() {
const options = this.options;
if (options.title.position === BOTTOM) {
this.layout.append(this.itemsContainer);
this.layout.append(this.title);
} else {
this.layout.append(this.title);
this.layout.append(this.itemsContainer);
}
}
}
setDefaultOptions(Legend, {
position: RIGHT,
data: [],
offsetX: 0,
offsetY: 0,
margin: getSpacing(2),
padding: getSpacing(5),
border: {
color: BLACK,
width: 0
},
item: {
cursor: POINTER,
spacing: 6
},
spacing: 6,
background: "",
zIndex: 1,
markers: {},
line: {
width: 20,
height: 2,
cursor: POINTER,
opacity: 1
},
area: {
type: SQUARE,
align: RIGHT,
width: 15,
height: 15,
}
});
export default Legend;