devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,080 lines (1,056 loc) • 40.2 kB
JavaScript
/**
* DevExtreme (esm/viz/components/legend.js)
* Version: 21.1.4
* Build date: Mon Jun 21 2021
*
* Copyright (c) 2012 - 2021 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import {
enumParser,
normalizeEnum,
patchFontOptions
} from "../core/utils";
import {
extend
} from "../../core/utils/extend";
import {
LayoutElement,
WrapperLayoutElement
} from "../core/layout_element";
import {
isDefined,
isFunction
} from "../../core/utils/type";
import {
Title
} from "../core/title";
import {
clone
} from "../../core/utils/object";
import {
noop
} from "../../core/utils/common";
import {
processHatchingAttrs,
getFuncIri
} from "../core/renderers/renderer";
import {
Deferred
} from "../../core/utils/deferred";
var _Number = Number;
var _math = Math;
var _round = _math.round;
var _max = _math.max;
var _min = _math.min;
var _ceil = _math.ceil;
var _isDefined = isDefined;
var _isFunction = isFunction;
var _enumParser = enumParser;
var _normalizeEnum = normalizeEnum;
var _extend = extend;
var DEFAULT_MARGIN = 10;
var DEFAULT_MARKER_HATCHING_WIDTH = 2;
var DEFAULT_MARKER_HATCHING_STEP = 5;
var CENTER = "center";
var RIGHT = "right";
var LEFT = "left";
var TOP = "top";
var BOTTOM = "bottom";
var HORIZONTAL = "horizontal";
var VERTICAL = "vertical";
var INSIDE = "inside";
var OUTSIDE = "outside";
var NONE = "none";
var HEIGHT = "height";
var WIDTH = "width";
var parseHorizontalAlignment = _enumParser([LEFT, CENTER, RIGHT]);
var parseVerticalAlignment = _enumParser([TOP, BOTTOM]);
var parseOrientation = _enumParser([VERTICAL, HORIZONTAL]);
var parseItemTextPosition = _enumParser([LEFT, RIGHT, TOP, BOTTOM]);
var parsePosition = _enumParser([OUTSIDE, INSIDE]);
var parseItemsAlignment = _enumParser([LEFT, CENTER, RIGHT]);
function getState(state, color, stateName) {
if (!state) {
return
}
var colorFromAction = state.fill;
return extend({}, {
state: stateName,
fill: colorFromAction === NONE ? color : colorFromAction,
opacity: state.opacity,
hatching: _extend({}, state.hatching, {
step: DEFAULT_MARKER_HATCHING_STEP,
width: DEFAULT_MARKER_HATCHING_WIDTH
})
})
}
function getAttributes(item, state, size) {
var attrs = processHatchingAttrs(item, state);
if (attrs.fill && 0 === attrs.fill.indexOf("DevExpress")) {
attrs.fill = getFuncIri(attrs.fill)
}
attrs.opacity = attrs.opacity >= 0 ? attrs.opacity : 1;
return extend({}, attrs, {
size: size
})
}
function parseMargins(options) {
var margin = options.margin;
if (margin >= 0) {
margin = _Number(options.margin);
margin = {
top: margin,
bottom: margin,
left: margin,
right: margin
}
} else {
margin = {
top: margin.top >= 0 ? _Number(margin.top) : DEFAULT_MARGIN,
bottom: margin.bottom >= 0 ? _Number(margin.bottom) : DEFAULT_MARGIN,
left: margin.left >= 0 ? _Number(margin.left) : DEFAULT_MARGIN,
right: margin.right >= 0 ? _Number(margin.right) : DEFAULT_MARGIN
}
}
options.margin = margin
}
function getSizeItem(options, markerBBox, labelBBox) {
var width;
var height;
switch (options.itemTextPosition) {
case LEFT:
case RIGHT:
width = markerBBox.width + 7 + labelBBox.width;
height = _max(markerBBox.height, labelBBox.height);
break;
case TOP:
case BOTTOM:
width = _max(markerBBox.width, labelBBox.width);
height = markerBBox.height + 4 + labelBBox.height
}
return {
width: width,
height: height
}
}
function calculateBBoxLabelAndMarker(markerBBox, labelBBox) {
var bBox = {};
bBox.left = _min(markerBBox.x, labelBBox.x);
bBox.top = _min(markerBBox.y, labelBBox.y);
bBox.right = _max(markerBBox.x + markerBBox.width, labelBBox.x + labelBBox.width);
bBox.bottom = _max(markerBBox.y + markerBBox.height, labelBBox.y + labelBBox.height);
return bBox
}
function applyMarkerState(id, idToIndexMap, items, stateName) {
var item = idToIndexMap && items[idToIndexMap[id]];
if (item) {
item.renderMarker(item.states[stateName])
}
}
function parseOptions(options, textField, allowInsidePosition) {
if (!options) {
return null
}
parseMargins(options);
options.horizontalAlignment = parseHorizontalAlignment(options.horizontalAlignment, RIGHT);
options.verticalAlignment = parseVerticalAlignment(options.verticalAlignment, options.horizontalAlignment === CENTER ? BOTTOM : TOP);
options.orientation = parseOrientation(options.orientation, options.horizontalAlignment === CENTER ? HORIZONTAL : VERTICAL);
options.itemTextPosition = parseItemTextPosition(options.itemTextPosition, options.orientation === HORIZONTAL ? BOTTOM : RIGHT);
options.position = allowInsidePosition ? parsePosition(options.position, OUTSIDE) : OUTSIDE;
options.itemsAlignment = parseItemsAlignment(options.itemsAlignment, null);
options.hoverMode = _normalizeEnum(options.hoverMode);
options.customizeText = _isFunction(options.customizeText) ? options.customizeText : function() {
return this[textField]
};
options.customizeHint = _isFunction(options.customizeHint) ? options.customizeHint : noop;
options._incidentOccurred = options._incidentOccurred || noop;
return options
}
function createSquareMarker(renderer, size) {
return renderer.rect(0, 0, size, size)
}
function createCircleMarker(renderer, size) {
return renderer.circle(size / 2, size / 2, size / 2)
}
function isCircle(type) {
return "circle" === _normalizeEnum(type)
}
function inRect(rect, x, y) {
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
}
function checkLinesSize(lines, layoutOptions, countItems, margins) {
var position = {
x: 0,
y: 0
};
var maxMeasureLength = 0;
var maxAltMeasureLength = 0;
var margin = 0;
if ("y" === layoutOptions.direction) {
margin = margins.top + margins.bottom
} else {
margin = margins.left + margins.right
}
lines.forEach((function(line, i) {
var firstItem = line[0];
var lineLength = line.length;
line.forEach((function(item, index) {
var offset = item.offset || layoutOptions.spacing;
position[layoutOptions.direction] += item[layoutOptions.measure] + (index !== lineLength - 1 ? offset : 0);
maxMeasureLength = _max(maxMeasureLength, position[layoutOptions.direction])
}));
position[layoutOptions.direction] = 0;
position[layoutOptions.altDirection] += firstItem[layoutOptions.altMeasure] + firstItem.altOffset || layoutOptions.altSpacing;
maxAltMeasureLength = _max(maxAltMeasureLength, position[layoutOptions.altDirection])
}));
if (maxMeasureLength + margin > layoutOptions.length) {
layoutOptions.countItem = decreaseItemCount(layoutOptions, countItems);
return true
}
}
function decreaseItemCount(layoutOptions, countItems) {
layoutOptions.altCountItem++;
return _ceil(countItems / layoutOptions.altCountItem)
}
function getLineLength(line, layoutOptions) {
return line.reduce((lineLength, item) => {
var offset = item.offset || layoutOptions.spacing;
return lineLength + item[layoutOptions.measure] + offset
}, 0)
}
function getMaxLineLength(lines, layoutOptions) {
return lines.reduce((maxLineLength, line) => _max(maxLineLength, getLineLength(line, layoutOptions)), 0)
}
function getInitPositionForDirection(line, layoutOptions, maxLineLength) {
var lineLength = getLineLength(line, layoutOptions);
var initPosition;
switch (layoutOptions.itemsAlignment) {
case RIGHT:
initPosition = maxLineLength - lineLength;
break;
case CENTER:
initPosition = (maxLineLength - lineLength) / 2;
break;
default:
initPosition = 0
}
return initPosition
}
function getPos(layoutOptions) {
switch (layoutOptions.itemTextPosition) {
case BOTTOM:
return {
horizontal: CENTER, vertical: TOP
};
case TOP:
return {
horizontal: CENTER, vertical: BOTTOM
};
case LEFT:
return {
horizontal: RIGHT, vertical: CENTER
};
case RIGHT:
return {
horizontal: LEFT, vertical: CENTER
}
}
}
function getLines(lines, layoutOptions, itemIndex) {
var tableLine = {};
if (itemIndex % layoutOptions.countItem === 0) {
if (layoutOptions.markerOffset) {
lines.push([], [])
} else {
lines.push([])
}
}
if (layoutOptions.markerOffset) {
tableLine.firstLine = lines[lines.length - 1];
tableLine.secondLine = lines[lines.length - 2]
} else {
tableLine.firstLine = tableLine.secondLine = lines[lines.length - 1]
}
return tableLine
}
function setMaxInLine(line, measure) {
var maxLineSize = line.reduce((maxLineSize, item) => {
var itemMeasure = item ? item[measure] : maxLineSize;
return _max(maxLineSize, itemMeasure)
}, 0);
line.forEach(item => {
if (item) {
item[measure] = maxLineSize
}
})
}
function transpose(array) {
var width = array.length;
var height = array[0].length;
var i;
var j;
var transposeArray = [];
for (i = 0; i < height; i++) {
transposeArray[i] = [];
for (j = 0; j < width; j++) {
transposeArray[i][j] = array[j][i]
}
}
return transposeArray
}
function getAlign(position) {
switch (position) {
case TOP:
case BOTTOM:
return CENTER;
case LEFT:
return RIGHT;
case RIGHT:
return LEFT
}
}
var getMarkerCreator = function(type) {
return isCircle(type) ? createCircleMarker : createSquareMarker
};
function getTitleHorizontalAlignment(options) {
if (options.horizontalAlignment === CENTER) {
return CENTER
} else if (options.itemTextPosition === RIGHT) {
return LEFT
} else if (options.itemTextPosition === LEFT) {
return RIGHT
} else {
return CENTER
}
}
export var Legend = function(settings) {
this._renderer = settings.renderer;
this._legendGroup = settings.group;
this._backgroundClass = settings.backgroundClass;
this._itemGroupClass = settings.itemGroupClass;
this._textField = settings.textField;
this._getCustomizeObject = settings.getFormatObject;
this._titleGroupClass = settings.titleGroupClass;
this._allowInsidePosition = settings.allowInsidePosition;
this._widget = settings.widget;
this._updated = false
};
var _Legend = Legend;
var legendPrototype = _Legend.prototype = clone(LayoutElement.prototype);
extend(legendPrototype, {
constructor: _Legend,
getOptions: function() {
return this._options
},
update: function() {
var data = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : [];
var options = arguments.length > 1 ? arguments[1] : void 0;
var themeManagerTitleOptions = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {};
var that = this;
options = that._options = parseOptions(options, that._textField, that._allowInsidePosition) || {};
var initMarkerSize = options.markerSize;
this._updated = true;
this._data = data.map(dataItem => {
dataItem.size = _Number(dataItem.size > 0 ? dataItem.size : initMarkerSize);
dataItem.marker = getAttributes(dataItem, dataItem.states.normal);
Object.defineProperty(dataItem.marker, "size", {
get: () => dataItem.size,
set(value) {
dataItem.size = value
}
});
Object.defineProperty(dataItem.marker, "opacity", {
get: () => dataItem.states.normal.opacity,
set(value) {
dataItem.states.normal.opacity = dataItem.states.hover.opacity = dataItem.states.selection.opacity = value
}
});
return dataItem
});
if (options.customizeItems) {
that._data = options.customizeItems(data.slice()) || data
}
that._boundingRect = {
width: 0,
height: 0,
x: 0,
y: 0
};
if (that.isVisible() && !that._title) {
that._title = new Title({
renderer: that._renderer,
cssClass: that._titleGroupClass,
root: that._legendGroup
})
}
if (that._title) {
var titleOptions = options.title;
themeManagerTitleOptions.horizontalAlignment = getTitleHorizontalAlignment(options);
that._title.update(themeManagerTitleOptions, titleOptions)
}
this.erase();
return that
},
isVisible: function() {
return this._options && this._options.visible
},
draw: function(width, height) {
var items = this._getItemData();
this.erase();
if (!(this.isVisible() && items && items.length)) {
return this
}
this._insideLegendGroup = this._renderer.g().enableLinks().append(this._legendGroup);
this._title.changeLink(this._insideLegendGroup);
this._createBackground();
if (this._title.hasText()) {
var horizontalPadding = this._background ? 2 * this._options.paddingLeftRight : 0;
this._title.draw(width - horizontalPadding, height)
}
this._markersGroup = this._renderer.g().attr({
class: this._itemGroupClass
}).append(this._insideLegendGroup);
this._createItems(items);
this._updateElementsPosition(width, height);
return this
},
_measureElements: function() {
var options = this._options;
var maxBBoxHeight = 0;
this._items.forEach(item => {
var labelBBox = item.label.getBBox();
var markerBBox = item.marker.getBBox();
item.markerBBox = markerBBox;
item.markerSize = Math.max(markerBBox.width, markerBBox.height);
var bBox = getSizeItem(options, markerBBox, labelBBox);
item.labelBBox = labelBBox;
item.bBox = bBox;
maxBBoxHeight = _max(maxBBoxHeight, bBox.height)
});
if (options.equalRowHeight) {
this._items.forEach(item => item.bBox.height = maxBBoxHeight)
}
},
_updateElementsPosition: function(width, height) {
var options = this._options;
this._size = {
width: width,
height: height
};
this._measureElements();
this._locateElements(options);
this._finalUpdate(options);
var size = this.getLayoutOptions();
if (size.width > width || size.height > height) {
this.freeSpace()
}
},
_createItems: function(items) {
var that = this;
var options = that._options;
var renderer = that._renderer;
var createMarker = getMarkerCreator(options.markerShape);
that._markersId = {};
var templateFunction = !options.markerTemplate ? (dataItem, group) => {
var attrs = dataItem.marker;
createMarker(renderer, attrs.size).attr({
fill: attrs.fill,
opacity: attrs.opacity
}).append({
element: group
})
} : options.markerTemplate;
var template = that._widget._getTemplate(templateFunction);
var markersGroup = that._markersGroup;
markersGroup.css(patchFontOptions(options.font));
that._deferredItems = [];
that._templatesGroups = [];
that._items = (items || []).map((dataItem, i) => {
var stateOfDataItem = dataItem.states;
var normalState = stateOfDataItem.normal;
var normalStateFill = normalState.fill;
dataItem.size = dataItem.marker.size;
var states = {
normal: extend(normalState, {
fill: normalStateFill || options.markerColor || options.defaultColor,
state: "normal"
}),
hover: getState(stateOfDataItem.hover, normalStateFill, "hovered"),
selection: getState(stateOfDataItem.selection, normalStateFill, "selected")
};
dataItem.states = states;
var itemGroup = renderer.g().append(markersGroup);
var markerGroup = renderer.g().attr({
class: "dxl-marker"
}).append(itemGroup);
that._deferredItems[i] = new Deferred;
that._templatesGroups.push(markerGroup);
var item = {
label: that._createLabel(dataItem, itemGroup),
marker: markerGroup,
renderer: renderer,
group: itemGroup,
tracker: {
id: dataItem.id,
argument: dataItem.argument,
argumentIndex: dataItem.argumentIndex
},
states: states,
itemTextPosition: options.itemTextPosition,
markerOffset: 0,
bBoxes: [],
renderMarker(state) {
dataItem.marker = getAttributes(item, state, dataItem.size);
markerGroup.clear();
template.render({
model: dataItem,
container: markerGroup.element,
onRendered: that._deferredItems[i].resolve
})
}
};
item.renderMarker(states.normal);
that._createHint(dataItem, itemGroup);
if (void 0 !== dataItem.id) {
that._markersId[dataItem.id] = i
}
return item
})
},
getTemplatesGroups: function() {
return this._templatesGroups || []
},
getTemplatesDef: function() {
return this._deferredItems || []
},
_getItemData: function() {
var items = this._data || [];
var options = this._options || {};
if (options.inverted) {
items = items.slice().reverse()
}
return items.filter(i => i.visible)
},
_finalUpdate: function(options) {
this._adjustBackgroundSettings(options);
this._setBoundingRect(options.margin)
},
erase: function() {
var insideLegendGroup = this._insideLegendGroup;
insideLegendGroup && insideLegendGroup.dispose();
this._insideLegendGroup = this._markersGroup = this._x1 = this._x2 = this._y2 = this._y2 = null;
return this
},
_locateElements: function(locationOptions) {
this._moveInInitialValues();
this._locateRowsColumns(locationOptions)
},
_moveInInitialValues: function() {
this._title.hasText() && this._title.move([0, 0]);
this._legendGroup && this._legendGroup.move(0, 0);
this._background && this._background.attr({
x: 0,
y: 0,
width: 0,
height: 0
})
},
applySelected: function(id) {
applyMarkerState(id, this._markersId, this._items, "selection");
return this
},
applyHover: function(id) {
applyMarkerState(id, this._markersId, this._items, "hover");
return this
},
resetItem: function(id) {
applyMarkerState(id, this._markersId, this._items, "normal");
return this
},
_createLabel: function(data, group) {
var labelFormatObject = this._getCustomizeObject(data);
var options = this._options;
var align = getAlign(options.itemTextPosition);
var text = options.customizeText.call(labelFormatObject, labelFormatObject);
var fontStyle = _isDefined(data.textOpacity) ? {
color: options.font.color,
opacity: data.textOpacity
} : {};
return this._renderer.text(text, 0, 0).css(patchFontOptions(fontStyle)).attr({
align: align,
class: options.cssClass
}).append(group)
},
_createHint: function(data, group) {
var labelFormatObject = this._getCustomizeObject(data);
var text = this._options.customizeHint.call(labelFormatObject, labelFormatObject);
if (_isDefined(text) && "" !== text) {
group.setTitle(text)
}
},
_createBackground: function() {
var isInside = this._options.position === INSIDE;
var color = this._options.backgroundColor;
var fill = color || (isInside ? this._options.containerBackgroundColor : NONE);
if (this._options.border.visible || (isInside || color) && color !== NONE) {
this._background = this._renderer.rect(0, 0, 0, 0).attr({
fill: fill,
class: this._backgroundClass
}).append(this._insideLegendGroup)
}
},
_locateRowsColumns: function(options) {
var iteration = 0;
var layoutOptions = this._getItemsLayoutOptions();
var countItems = this._items.length;
var lines;
do {
lines = [];
this._createLines(lines, layoutOptions);
this._alignLines(lines, layoutOptions);
iteration++
} while (checkLinesSize(lines, layoutOptions, countItems, options.margin) && iteration < countItems);
this._applyItemPosition(lines, layoutOptions)
},
_createLines: function(lines, layoutOptions) {
this._items.forEach((item, i) => {
var tableLine = getLines(lines, layoutOptions, i);
var labelBox = {
width: item.labelBBox.width,
height: item.labelBBox.height,
element: item.label,
bBox: item.labelBBox,
pos: getPos(layoutOptions),
itemIndex: i
};
var markerBox = {
width: item.markerBBox.width,
height: item.markerBBox.height,
element: item.marker,
pos: {
horizontal: CENTER,
vertical: CENTER
},
bBox: {
width: item.markerBBox.width,
height: item.markerBBox.height,
x: item.markerBBox.x,
y: item.markerBBox.y
},
itemIndex: i
};
var firstItem;
var secondItem;
var offsetDirection = layoutOptions.markerOffset ? "altOffset" : "offset";
if (layoutOptions.inverseLabelPosition) {
firstItem = labelBox;
secondItem = markerBox
} else {
firstItem = markerBox;
secondItem = labelBox
}
firstItem[offsetDirection] = layoutOptions.labelOffset;
tableLine.secondLine.push(firstItem);
tableLine.firstLine.push(secondItem)
})
},
_alignLines: function(lines, layoutOptions) {
var i;
var measure = layoutOptions.altMeasure;
lines.forEach(line => setMaxInLine(line, measure));
measure = layoutOptions.measure;
if (layoutOptions.itemsAlignment) {
if (layoutOptions.markerOffset) {
for (i = 0; i < lines.length;) {
transpose([lines[i++], lines[i++]]).forEach(processLine)
}
}
} else {
transpose(lines).forEach(processLine)
}
function processLine(line) {
setMaxInLine(line, measure)
}
},
_applyItemPosition: function(lines, layoutOptions) {
var that = this;
var position = {
x: 0,
y: 0
};
var maxLineLength = getMaxLineLength(lines, layoutOptions);
lines.forEach(line => {
var firstItem = line[0];
var altOffset = firstItem.altOffset || layoutOptions.altSpacing;
position[layoutOptions.direction] = getInitPositionForDirection(line, layoutOptions, maxLineLength);
line.forEach(item => {
var offset = item.offset || layoutOptions.spacing;
var wrap = new WrapperLayoutElement(item.element, item.bBox);
var itemBBoxOptions = {
x: position.x,
y: position.y,
width: item.width,
height: item.height
};
var itemBBox = new WrapperLayoutElement(null, itemBBoxOptions);
var itemLegend = that._items[item.itemIndex];
wrap.position({
of: itemBBox,
my: item.pos,
at: item.pos
});
itemLegend.bBoxes.push(itemBBox);
position[layoutOptions.direction] += item[layoutOptions.measure] + offset
});
position[layoutOptions.altDirection] += firstItem[layoutOptions.altMeasure] + altOffset
});
this._items.forEach(item => {
var itemBBox = calculateBBoxLabelAndMarker(item.bBoxes[0].getLayoutOptions(), item.bBoxes[1].getLayoutOptions());
var horizontal = that._options.columnItemSpacing / 2;
var vertical = that._options.rowItemSpacing / 2;
item.tracker.left = itemBBox.left - horizontal;
item.tracker.right = itemBBox.right + horizontal;
item.tracker.top = itemBBox.top - vertical;
item.tracker.bottom = itemBBox.bottom + vertical
})
},
_getItemsLayoutOptions: function() {
var options = this._options;
var orientation = options.orientation;
var layoutOptions = {
itemsAlignment: options.itemsAlignment,
orientation: options.orientation
};
var width = this._size.width - (this._background ? 2 * options.paddingLeftRight : 0);
var height = this._size.height - (this._background ? 2 * options.paddingTopBottom : 0);
if (orientation === HORIZONTAL) {
layoutOptions.length = width;
layoutOptions.spacing = options.columnItemSpacing;
layoutOptions.direction = "x";
layoutOptions.measure = WIDTH;
layoutOptions.altMeasure = HEIGHT;
layoutOptions.altDirection = "y";
layoutOptions.altSpacing = options.rowItemSpacing;
layoutOptions.countItem = options.columnCount;
layoutOptions.altCountItem = options.rowCount;
layoutOptions.marginTextLabel = 4;
layoutOptions.labelOffset = 7;
if (options.itemTextPosition === BOTTOM || options.itemTextPosition === TOP) {
layoutOptions.labelOffset = 4;
layoutOptions.markerOffset = true
}
} else {
layoutOptions.length = height;
layoutOptions.spacing = options.rowItemSpacing;
layoutOptions.direction = "y";
layoutOptions.measure = HEIGHT;
layoutOptions.altMeasure = WIDTH;
layoutOptions.altDirection = "x";
layoutOptions.altSpacing = options.columnItemSpacing;
layoutOptions.countItem = options.rowCount;
layoutOptions.altCountItem = options.columnCount;
layoutOptions.marginTextLabel = 7;
layoutOptions.labelOffset = 4;
if (options.itemTextPosition === RIGHT || options.itemTextPosition === LEFT) {
layoutOptions.labelOffset = 7;
layoutOptions.markerOffset = true
}
}
if (!layoutOptions.countItem) {
if (layoutOptions.altCountItem) {
layoutOptions.countItem = _ceil(this._items.length / layoutOptions.altCountItem)
} else {
layoutOptions.countItem = this._items.length
}
}
if (options.itemTextPosition === TOP || options.itemTextPosition === LEFT) {
layoutOptions.inverseLabelPosition = true
}
layoutOptions.itemTextPosition = options.itemTextPosition;
layoutOptions.altCountItem = layoutOptions.altCountItem || _ceil(this._items.length / layoutOptions.countItem);
return layoutOptions
},
_adjustBackgroundSettings: function(locationOptions) {
if (!this._background) {
return
}
var border = locationOptions.border;
var legendBox = this._calculateTotalBox();
var backgroundSettings = {
x: _round(legendBox.x - locationOptions.paddingLeftRight),
y: _round(legendBox.y - locationOptions.paddingTopBottom),
width: _round(legendBox.width) + 2 * locationOptions.paddingLeftRight,
height: _round(legendBox.height),
opacity: locationOptions.backgroundOpacity
};
if (border.visible && border.width && border.color && border.color !== NONE) {
backgroundSettings["stroke-width"] = border.width;
backgroundSettings.stroke = border.color;
backgroundSettings["stroke-opacity"] = border.opacity;
backgroundSettings.dashStyle = border.dashStyle;
backgroundSettings.rx = border.cornerRadius || 0;
backgroundSettings.ry = border.cornerRadius || 0
}
this._background.attr(backgroundSettings)
},
_setBoundingRect: function(margin) {
if (!this._insideLegendGroup) {
return
}
var box = this._calculateTotalBox();
box.height += margin.top + margin.bottom;
box.widthWithoutMargins = box.width;
box.width += margin.left + margin.right;
box.x -= margin.left;
box.y -= margin.top;
this._boundingRect = box
},
_calculateTotalBox: function() {
var markerBox = this._markersGroup.getBBox();
var titleBox = this._title.getCorrectedLayoutOptions();
var box = this._insideLegendGroup.getBBox();
var verticalPadding = this._background ? 2 * this._options.paddingTopBottom : 0;
box.height = markerBox.height + titleBox.height + verticalPadding;
titleBox.width > box.width && (box.width = titleBox.width);
return box
},
getActionCallback: function(point) {
var that = this;
if (that._options.visible) {
return function(act) {
that[act](point.index)
}
} else {
return noop
}
},
getLayoutOptions: function() {
var options = this._options;
var boundingRect = this._insideLegendGroup ? this._boundingRect : {
width: 0,
height: 0,
x: 0,
y: 0
};
if (options) {
boundingRect.verticalAlignment = options.verticalAlignment;
boundingRect.horizontalAlignment = options.horizontalAlignment;
if (options.orientation === HORIZONTAL) {
boundingRect.cutLayoutSide = options.verticalAlignment;
boundingRect.cutSide = "vertical"
} else if (options.horizontalAlignment === CENTER) {
boundingRect.cutLayoutSide = options.verticalAlignment;
boundingRect.cutSide = "vertical"
} else {
boundingRect.cutLayoutSide = options.horizontalAlignment;
boundingRect.cutSide = "horizontal"
}
boundingRect.position = {
horizontal: options.horizontalAlignment,
vertical: options.verticalAlignment
};
return boundingRect
}
return null
},
shift: function(x, y) {
var box = {};
if (this._insideLegendGroup) {
this._insideLegendGroup.attr({
translateX: x - this._boundingRect.x,
translateY: y - this._boundingRect.y
})
}
this._title && this._shiftTitle(this._boundingRect.widthWithoutMargins);
this._markersGroup && this._shiftMarkers();
if (this._insideLegendGroup) {
box = this._legendGroup.getBBox()
}
this._x1 = box.x;
this._y1 = box.y;
this._x2 = box.x + box.width;
this._y2 = box.y + box.height;
return this
},
_shiftTitle: function(boxWidth) {
var title = this._title;
var titleBox = title.getCorrectedLayoutOptions();
if (!titleBox || !title.hasText()) {
return
}
var width = boxWidth - (this._background ? 2 * this._options.paddingLeftRight : 0);
var titleOptions = title.getOptions();
var titleY = titleBox.y + titleOptions.margin.top;
var titleX = 0;
if (titleOptions.verticalAlignment === BOTTOM && this._markersGroup) {
titleY += this._markersGroup.getBBox().height
}
if (titleOptions.horizontalAlignment === RIGHT) {
titleX = width - titleBox.width
} else if (titleOptions.horizontalAlignment === CENTER) {
titleX = (width - titleBox.width) / 2
}
title.shift(titleX, titleY)
},
_shiftMarkers: function() {
var titleBox = this._title.getLayoutOptions();
var markerBox = this._markersGroup.getBBox();
var titleOptions = this._title.getOptions() || {};
var center = 0;
var y = 0;
if (titleBox.width > markerBox.width && this._options.horizontalAlignment === CENTER) {
center = titleBox.width / 2 - markerBox.width / 2
}
if (titleOptions.verticalAlignment === TOP) {
y = titleBox.height
}
if (0 !== center || 0 !== y) {
this._markersGroup.attr({
translateX: center,
translateY: y
});
this._items.forEach(item => {
item.tracker.left += center;
item.tracker.right += center;
item.tracker.top += y;
item.tracker.bottom += y
})
}
},
getPosition: function() {
return this._options.position
},
coordsIn: function(x, y) {
return x >= this._x1 && x <= this._x2 && y >= this._y1 && y <= this._y2
},
getItemByCoord: function(x, y) {
var items = this._items;
var legendGroup = this._insideLegendGroup;
x -= legendGroup.attr("translateX");
y -= legendGroup.attr("translateY");
for (var i = 0; i < items.length; i++) {
if (inRect(items[i].tracker, x, y)) {
return items[i].tracker
}
}
return null
},
dispose: function() {
this._title && this._title.dispose();
this._legendGroup = this._insideLegendGroup = this._title = this._renderer = this._options = this._data = this._items = null;
return this
},
layoutOptions: function() {
if (!this.isVisible()) {
return null
}
var pos = this.getLayoutOptions();
return {
horizontalAlignment: this._options.horizontalAlignment,
verticalAlignment: this._options.verticalAlignment,
side: pos.cutSide,
priority: 1,
position: this.getPosition()
}
},
measure: function(size) {
if (this._updated || !this._insideLegendGroup) {
this.draw(size[0], size[1]);
this._updated = false
} else {
this._items.forEach(item => {
item.bBoxes = []
});
this._updateElementsPosition(size[0], size[1])
}
var rect = this.getLayoutOptions();
return [rect.width, rect.height]
},
move: function(rect) {
this.shift(rect[0], rect[1])
},
freeSpace: function() {
this._options._incidentOccurred("W2104");
this.erase()
}
});
export var plugin = {
name: "legend",
init: function() {
var group = this._renderer.g().attr({
class: this._rootClassPrefix + "-legend"
}).enableLinks().append(this._renderer.root);
this._legend = new Legend({
renderer: this._renderer,
group: group,
widget: this,
itemGroupClass: this._rootClassPrefix + "-item",
titleGroupClass: this._rootClassPrefix + "-title",
textField: "text",
getFormatObject: function(data) {
return {
item: data.item,
text: data.text
}
}
});
this._layout.add(this._legend)
},
extenders: {
_applyTilesAppearance: function() {
var that = this;
this._items.forEach((function(item) {
that._applyLegendItemStyle(item.id, item.getState())
}))
},
_buildNodes: function() {
this._createLegendItems()
}
},
members: {
_applyLegendItemStyle: function(id, state) {
var legend = this._legend;
switch (state) {
case "hover":
legend.applyHover(id);
break;
case "selection":
legend.applySelected(id);
break;
default:
legend.resetItem(id)
}
},
_createLegendItems: function() {
if (this._legend.update(this._getLegendData(), this._getOption("legend"), this._themeManager.theme("legend").title)) {
this._requestChange(["LAYOUT"])
}
}
},
dispose: function() {
this._legend.dispose()
},
customize: function(constructor) {
constructor.prototype._proxyData.push((function(x, y) {
if (this._legend.coordsIn(x, y)) {
var item = this._legend.getItemByCoord(x, y);
if (item) {
return {
id: item.id,
type: "legend"
}
}
}
}));
constructor.addChange({
code: "LEGEND",
handler: function() {
this._createLegendItems()
},
isThemeDependent: true,
option: "legend",
isOptionChange: true
})
}
};