ag-charts-community
Version:
Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue
1,104 lines • 46.9 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
var scene_1 = require("../scene/scene");
var group_1 = require("../scene/group");
var padding_1 = require("../util/padding");
var shape_1 = require("../scene/shape/shape");
var rect_1 = require("../scene/shape/rect");
var legend_1 = require("./legend");
var bbox_1 = require("../scene/bbox");
var array_1 = require("../util/array");
var sizeMonitor_1 = require("../util/sizeMonitor");
var observable_1 = require("../util/observable");
var id_1 = require("../util/id");
var labelPlacement_1 = require("../util/labelPlacement");
var defaultTooltipCss = "\n.ag-chart-tooltip {\n display: table;\n position: absolute;\n user-select: none;\n pointer-events: none;\n white-space: nowrap;\n z-index: 99999;\n font: 12px Verdana, sans-serif;\n color: black;\n background: rgb(244, 244, 244);\n border-radius: 5px;\n box-shadow: 0 0 1px rgba(3, 3, 3, 0.7), 0.5vh 0.5vh 1vh rgba(3, 3, 3, 0.25);\n}\n\n.ag-chart-tooltip-hidden {\n top: -10000px !important;\n}\n\n.ag-chart-tooltip-title {\n font-weight: bold;\n padding: 7px;\n border-top-left-radius: 5px;\n border-top-right-radius: 5px;\n color: white;\n background-color: #888888;\n border-top-left-radius: 5px;\n border-top-right-radius: 5px;\n}\n\n.ag-chart-tooltip-content {\n padding: 7px;\n line-height: 1.7em;\n border-bottom-left-radius: 5px;\n border-bottom-right-radius: 5px;\n overflow: hidden;\n}\n\n.ag-chart-tooltip-content:empty {\n padding: 0;\n height: 7px;\n}\n\n.ag-chart-tooltip-arrow::before {\n content: \"\";\n\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n\n border: 6px solid #989898;\n\n border-left-color: transparent;\n border-right-color: transparent;\n border-top-color: #989898;\n border-bottom-color: transparent;\n\n width: 0;\n height: 0;\n\n margin: 0 auto;\n}\n\n.ag-chart-tooltip-arrow::after {\n content: \"\";\n\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n\n border: 5px solid black;\n\n border-left-color: transparent;\n border-right-color: transparent;\n border-top-color: rgb(244, 244, 244);\n border-bottom-color: transparent;\n\n width: 0;\n height: 0;\n\n margin: 0 auto;\n}\n\n.ag-chart-wrapper {\n box-sizing: border-box;\n overflow: hidden;\n}\n";
function toTooltipHtml(input, defaults) {
if (typeof input === 'string') {
return input;
}
defaults = defaults || {};
var _a = input.content, content = _a === void 0 ? defaults.content || '' : _a, _b = input.title, title = _b === void 0 ? defaults.title || undefined : _b, _c = input.color, color = _c === void 0 ? defaults.color || 'white' : _c, _d = input.backgroundColor, backgroundColor = _d === void 0 ? defaults.backgroundColor || '#888' : _d;
var titleHtml = title ? "<div class=\"" + Chart.defaultTooltipClass + "-title\"\n style=\"color: " + color + "; background-color: " + backgroundColor + "\">" + title + "</div>" : '';
return titleHtml + "<div class=\"" + Chart.defaultTooltipClass + "-content\">" + content + "</div>";
}
exports.toTooltipHtml = toTooltipHtml;
var ChartTooltip = /** @class */ (function (_super) {
__extends(ChartTooltip, _super);
function ChartTooltip(chart, document) {
var _this = _super.call(this) || this;
_this.enabled = true;
_this.class = Chart.defaultTooltipClass;
_this.delay = 0;
/**
* If `true`, the tooltip will be shown for the marker closest to the mouse cursor.
* Only has effect on series with markers.
*/
_this.tracking = true;
_this.showTimeout = 0;
_this.constrained = false;
_this.chart = chart;
_this.class = '';
var tooltipRoot = document.body;
var element = document.createElement('div');
_this.element = tooltipRoot.appendChild(element);
// Detect when the chart becomes invisible and hide the tooltip as well.
if (window.IntersectionObserver) {
var target_1 = _this.chart.scene.canvas.element;
var observer = new IntersectionObserver(function (entries) {
var e_1, _a;
try {
for (var entries_1 = __values(entries), entries_1_1 = entries_1.next(); !entries_1_1.done; entries_1_1 = entries_1.next()) {
var entry = entries_1_1.value;
if (entry.target === target_1 && entry.intersectionRatio === 0) {
_this.toggle(false);
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (entries_1_1 && !entries_1_1.done && (_a = entries_1.return)) _a.call(entries_1);
}
finally { if (e_1) throw e_1.error; }
}
}, { root: tooltipRoot });
observer.observe(target_1);
_this.observer = observer;
}
return _this;
}
ChartTooltip.prototype.destroy = function () {
var parentNode = this.element.parentNode;
if (parentNode) {
parentNode.removeChild(this.element);
}
if (this.observer) {
this.observer.unobserve(this.chart.scene.canvas.element);
}
};
ChartTooltip.prototype.isVisible = function () {
var element = this.element;
if (element.classList) { // if not IE11
return !element.classList.contains(Chart.defaultTooltipClass + '-hidden');
}
// IE11 part.
var classes = element.getAttribute('class');
if (classes) {
return classes.split(' ').indexOf(Chart.defaultTooltipClass + '-hidden') < 0;
}
return false;
};
ChartTooltip.prototype.updateClass = function (visible, constrained) {
var classList = [Chart.defaultTooltipClass, this.class];
if (visible !== true) {
classList.push(Chart.defaultTooltipClass + "-hidden");
}
if (constrained !== true) {
classList.push(Chart.defaultTooltipClass + "-arrow");
}
this.element.setAttribute('class', classList.join(' '));
};
/**
* Shows tooltip at the given event's coordinates.
* If the `html` parameter is missing, moves the existing tooltip to the new position.
*/
ChartTooltip.prototype.show = function (meta, html, instantly) {
var _this = this;
if (instantly === void 0) { instantly = false; }
var el = this.element;
if (html !== undefined) {
el.innerHTML = html;
}
else if (!el.innerHTML) {
return;
}
var left = meta.pageX - el.clientWidth / 2;
var top = meta.pageY - el.clientHeight - 8;
this.constrained = false;
if (this.chart.container) {
var tooltipRect = el.getBoundingClientRect();
var minLeft = 0;
var maxLeft = window.innerWidth - tooltipRect.width - 1;
if (left < minLeft) {
left = minLeft;
this.updateClass(true, this.constrained = true);
}
else if (left > maxLeft) {
left = maxLeft;
this.updateClass(true, this.constrained = true);
}
if (top < window.pageYOffset) {
top = meta.pageY + 20;
this.updateClass(true, this.constrained = true);
}
}
el.style.left = Math.round(left) + "px";
el.style.top = Math.round(top) + "px";
if (this.delay > 0 && !instantly) {
this.toggle(false);
this.showTimeout = window.setTimeout(function () {
_this.toggle(true);
}, this.delay);
return;
}
this.toggle(true);
};
ChartTooltip.prototype.toggle = function (visible) {
if (!visible) {
window.clearTimeout(this.showTimeout);
if (this.chart.lastPick && !this.delay) {
this.chart.dehighlightDatum();
this.chart.lastPick = undefined;
}
}
this.updateClass(visible, this.constrained);
};
__decorate([
observable_1.reactive()
], ChartTooltip.prototype, "enabled", void 0);
__decorate([
observable_1.reactive()
], ChartTooltip.prototype, "class", void 0);
__decorate([
observable_1.reactive()
], ChartTooltip.prototype, "delay", void 0);
__decorate([
observable_1.reactive()
], ChartTooltip.prototype, "tracking", void 0);
return ChartTooltip;
}(observable_1.Observable));
exports.ChartTooltip = ChartTooltip;
var Chart = /** @class */ (function (_super) {
__extends(Chart, _super);
function Chart(document) {
if (document === void 0) { document = window.document; }
var _this = _super.call(this) || this;
_this.id = id_1.createId(_this);
_this.background = new rect_1.Rect();
_this.legend = new legend_1.Legend();
_this.legendAutoPadding = new padding_1.Padding();
_this.captionAutoPadding = 0; // top padding only
_this._container = undefined;
_this._data = [];
_this._autoSize = false;
_this.padding = new padding_1.Padding(20);
_this._axes = [];
_this._series = [];
_this._axesChanged = false;
_this._seriesChanged = false;
_this.layoutCallbackId = 0;
_this._performLayout = function () {
_this.layoutCallbackId = 0;
_this.background.width = _this.width;
_this.background.height = _this.height;
_this.performLayout();
if (!_this.layoutPending) {
_this.fireEvent({ type: 'layoutDone' });
}
};
_this.dataCallbackId = 0;
_this.nodeData = new Map();
_this.updateCallbackId = 0;
_this.legendBBox = new bbox_1.BBox(0, 0, 0, 0);
_this._onMouseDown = _this.onMouseDown.bind(_this);
_this._onMouseMove = _this.onMouseMove.bind(_this);
_this._onMouseUp = _this.onMouseUp.bind(_this);
_this._onMouseOut = _this.onMouseOut.bind(_this);
_this._onClick = _this.onClick.bind(_this);
_this.pointerInsideLegend = false;
var root = new group_1.Group();
var background = _this.background;
background.fill = 'white';
root.appendChild(background);
var element = _this._element = document.createElement('div');
element.setAttribute('class', 'ag-chart-wrapper');
var scene = new scene_1.Scene(document);
_this.scene = scene;
scene.root = root;
scene.container = element;
_this.autoSize = true;
_this.padding.addEventListener('layoutChange', _this.scheduleLayout, _this);
var legend = _this.legend;
legend.addEventListener('layoutChange', _this.scheduleLayout, _this);
legend.item.label.addPropertyListener('formatter', _this.updateLegend, _this);
legend.addPropertyListener('position', _this.onLegendPositionChange, _this);
_this.tooltip = new ChartTooltip(_this, document);
_this.tooltip.addPropertyListener('class', function () { return _this.tooltip.toggle(); });
if (Chart.tooltipDocuments.indexOf(document) < 0) {
var styleElement = document.createElement('style');
styleElement.innerHTML = defaultTooltipCss;
// Make sure the default tooltip style goes before other styles so it can be overridden.
document.head.insertBefore(styleElement, document.head.querySelector('style'));
Chart.tooltipDocuments.push(document);
}
_this.setupDomListeners(scene.canvas.element);
_this.addPropertyListener('title', _this.onCaptionChange);
_this.addPropertyListener('subtitle', _this.onCaptionChange);
_this.addEventListener('layoutChange', _this.scheduleLayout);
return _this;
}
Object.defineProperty(Chart.prototype, "container", {
get: function () {
return this._container;
},
set: function (value) {
if (this._container !== value) {
var parentNode = this.element.parentNode;
if (parentNode != null) {
parentNode.removeChild(this.element);
}
if (value) {
value.appendChild(this.element);
}
this._container = value;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "data", {
get: function () {
return this._data;
},
set: function (data) {
this._data = data;
this.series.forEach(function (series) { return series.data = data; });
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "width", {
get: function () {
return this.scene.width;
},
set: function (value) {
this.autoSize = false;
if (this.width !== value) {
this.scene.resize(value, this.height);
this.fireEvent({ type: 'layoutChange' });
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "height", {
get: function () {
return this.scene.height;
},
set: function (value) {
this.autoSize = false;
if (this.height !== value) {
this.scene.resize(this.width, value);
this.fireEvent({ type: 'layoutChange' });
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "autoSize", {
get: function () {
return this._autoSize;
},
set: function (value) {
if (this._autoSize !== value) {
this._autoSize = value;
var style = this.element.style;
if (value) {
var chart_1 = this; // capture `this` for IE11
sizeMonitor_1.SizeMonitor.observe(this.element, function (size) {
if (size.width !== chart_1.width || size.height !== chart_1.height) {
chart_1.scene.resize(size.width, size.height);
chart_1.fireEvent({ type: 'layoutChange' });
}
});
style.display = 'block';
style.width = '100%';
style.height = '100%';
}
else {
sizeMonitor_1.SizeMonitor.unobserve(this.element);
style.display = 'inline-block';
style.width = 'auto';
style.height = 'auto';
}
}
},
enumerable: true,
configurable: true
});
Chart.prototype.download = function (fileName) {
this.scene.download(fileName);
};
Chart.prototype.destroy = function () {
this.tooltip.destroy();
sizeMonitor_1.SizeMonitor.unobserve(this.element);
this.container = undefined;
this.cleanupDomListeners(this.scene.canvas.element);
this.scene.container = undefined;
};
Chart.prototype.onLegendPositionChange = function () {
this.legendAutoPadding.clear();
this.layoutPending = true;
};
Chart.prototype.onCaptionChange = function (event) {
var value = event.value, oldValue = event.oldValue;
if (oldValue) {
oldValue.removeEventListener('change', this.scheduleLayout, this);
this.scene.root.removeChild(oldValue.node);
}
if (value) {
value.addEventListener('change', this.scheduleLayout, this);
this.scene.root.appendChild(value.node);
}
};
Object.defineProperty(Chart.prototype, "element", {
get: function () {
return this._element;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "axes", {
get: function () {
return this._axes;
},
set: function (values) {
var _this = this;
this._axes.forEach(function (axis) { return _this.detachAxis(axis); });
// make linked axes go after the regular ones (simulates stable sort by `linkedTo` property)
this._axes = values.filter(function (a) { return !a.linkedTo; }).concat(values.filter(function (a) { return a.linkedTo; }));
this._axes.forEach(function (axis) { return _this.attachAxis(axis); });
this.axesChanged = true;
},
enumerable: true,
configurable: true
});
Chart.prototype.attachAxis = function (axis) {
this.scene.root.insertBefore(axis.group, this.seriesRoot);
};
Chart.prototype.detachAxis = function (axis) {
this.scene.root.removeChild(axis.group);
};
Object.defineProperty(Chart.prototype, "series", {
get: function () {
return this._series;
},
set: function (values) {
var _this = this;
this.removeAllSeries();
values.forEach(function (series) { return _this.addSeries(series); });
},
enumerable: true,
configurable: true
});
Chart.prototype.scheduleLayout = function () {
this.layoutPending = true;
};
Chart.prototype.scheduleData = function () {
// To prevent the chart from thinking the cursor is over the same node
// after a change to data (the nodes are reused on data changes).
this.dehighlightDatum();
this.dataPending = true;
};
Chart.prototype.addSeries = function (series, before) {
var _a = this, allSeries = _a.series, seriesRoot = _a.seriesRoot;
var canAdd = allSeries.indexOf(series) < 0;
if (canAdd) {
var beforeIndex = before ? allSeries.indexOf(before) : -1;
if (beforeIndex >= 0) {
allSeries.splice(beforeIndex, 0, series);
seriesRoot.insertBefore(series.group, before.group);
}
else {
allSeries.push(series);
seriesRoot.append(series.group);
}
this.initSeries(series);
this.seriesChanged = true;
this.axesChanged = true;
return true;
}
return false;
};
Chart.prototype.initSeries = function (series) {
series.chart = this;
if (!series.data) {
series.data = this.data;
}
series.addEventListener('layoutChange', this.scheduleLayout, this);
series.addEventListener('dataChange', this.scheduleData, this);
series.addEventListener('legendChange', this.updateLegend, this);
series.addEventListener('nodeClick', this.onSeriesNodeClick, this);
};
Chart.prototype.freeSeries = function (series) {
series.chart = undefined;
series.removeEventListener('layoutChange', this.scheduleLayout, this);
series.removeEventListener('dataChange', this.scheduleData, this);
series.removeEventListener('legendChange', this.updateLegend, this);
series.removeEventListener('nodeClick', this.onSeriesNodeClick, this);
};
Chart.prototype.addSeriesAfter = function (series, after) {
var _a = this, allSeries = _a.series, seriesRoot = _a.seriesRoot;
var canAdd = allSeries.indexOf(series) < 0;
if (canAdd) {
var afterIndex = after ? this.series.indexOf(after) : -1;
if (afterIndex >= 0) {
if (afterIndex + 1 < allSeries.length) {
seriesRoot.insertBefore(series.group, allSeries[afterIndex + 1].group);
}
else {
seriesRoot.append(series.group);
}
this.initSeries(series);
allSeries.splice(afterIndex + 1, 0, series);
}
else {
if (allSeries.length > 0) {
seriesRoot.insertBefore(series.group, allSeries[0].group);
}
else {
seriesRoot.append(series.group);
}
this.initSeries(series);
allSeries.unshift(series);
}
this.seriesChanged = true;
this.axesChanged = true;
}
return false;
};
Chart.prototype.removeSeries = function (series) {
var index = this.series.indexOf(series);
if (index >= 0) {
this.series.splice(index, 1);
this.freeSeries(series);
this.seriesRoot.removeChild(series.group);
this.seriesChanged = true;
return true;
}
return false;
};
Chart.prototype.removeAllSeries = function () {
var _this = this;
this.series.forEach(function (series) {
_this.freeSeries(series);
_this.seriesRoot.removeChild(series.group);
});
this._series = []; // using `_series` instead of `series` to prevent infinite recursion
this.seriesChanged = true;
};
Chart.prototype.assignSeriesToAxes = function () {
var _this = this;
this.axes.forEach(function (axis) {
var axisName = axis.direction + 'Axis';
var boundSeries = [];
_this.series.forEach(function (series) {
if (series[axisName] === axis) {
boundSeries.push(series);
}
});
axis.boundSeries = boundSeries;
});
this.seriesChanged = false;
};
Chart.prototype.assignAxesToSeries = function (force) {
var _this = this;
if (force === void 0) { force = false; }
// This method has to run before `assignSeriesToAxes`.
var directionToAxesMap = {};
this.axes.forEach(function (axis) {
var direction = axis.direction;
var directionAxes = directionToAxesMap[direction] || (directionToAxesMap[direction] = []);
directionAxes.push(axis);
});
this.series.forEach(function (series) {
series.directions.forEach(function (direction) {
var axisName = direction + 'Axis';
if (!series[axisName] || force) {
var directionAxes = directionToAxesMap[direction];
if (directionAxes) {
var axis = _this.findMatchingAxis(directionAxes, series.getKeys(direction));
if (axis) {
series[axisName] = axis;
}
}
}
});
});
this.axesChanged = false;
};
Chart.prototype.findMatchingAxis = function (directionAxes, directionKeys) {
for (var i = 0; i < directionAxes.length; i++) {
var axis = directionAxes[i];
var axisKeys = axis.keys;
if (!axisKeys.length) {
return axis;
}
else if (directionKeys) {
for (var j = 0; j < directionKeys.length; j++) {
if (axisKeys.indexOf(directionKeys[j]) >= 0) {
return axis;
}
}
}
}
};
Object.defineProperty(Chart.prototype, "axesChanged", {
get: function () {
return this._axesChanged;
},
set: function (value) {
this._axesChanged = value;
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "seriesChanged", {
get: function () {
return this._seriesChanged;
},
set: function (value) {
this._seriesChanged = value;
if (value) {
this.dataPending = true;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "layoutPending", {
/**
* Only `true` while we are waiting for the layout to start.
* This will be `false` if the layout has already started and is ongoing.
*/
get: function () {
return !!this.layoutCallbackId;
},
set: function (value) {
if (value) {
if (!(this.layoutCallbackId || this.dataPending)) {
this.layoutCallbackId = requestAnimationFrame(this._performLayout);
this.series.forEach(function (s) { return s.nodeDataPending = true; });
}
}
else if (this.layoutCallbackId) {
cancelAnimationFrame(this.layoutCallbackId);
this.layoutCallbackId = 0;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Chart.prototype, "dataPending", {
get: function () {
return !!this.dataCallbackId;
},
set: function (value) {
var _this = this;
if (this.dataCallbackId) {
clearTimeout(this.dataCallbackId);
this.dataCallbackId = 0;
}
if (value) {
this.dataCallbackId = window.setTimeout(function () {
_this.dataPending = false;
_this.processData();
}, 0);
}
},
enumerable: true,
configurable: true
});
Chart.prototype.processData = function () {
this.layoutPending = false;
if (this.axesChanged) {
this.assignAxesToSeries(true);
this.assignSeriesToAxes();
}
if (this.seriesChanged) {
this.assignSeriesToAxes();
}
this.series.forEach(function (s) { return s.processData(); });
this.updateLegend(); // sets legend data which schedules a layout
this.layoutPending = true;
};
Chart.prototype.createNodeData = function () {
var _this = this;
this.nodeData.clear();
this.series.forEach(function (s) {
var data = s.visible ? s.createNodeData() : [];
_this.nodeData.set(s, data);
});
};
Chart.prototype.placeLabels = function () {
var series = [];
var data = [];
this.nodeData.forEach(function (d, s) {
if (s.visible && s.label.enabled) {
series.push(s);
data.push(s.getLabelData());
}
});
var seriesRect = this.seriesRect;
var labels = seriesRect
? labelPlacement_1.placeLabels(data, { x: 0, y: 0, width: seriesRect.width, height: seriesRect.height })
: [];
return new Map(labels.map(function (l, i) { return [series[i], l]; }));
};
Chart.prototype.updateLegend = function () {
var legendData = [];
this.series.filter(function (s) { return s.showInLegend; }).forEach(function (series) { return series.listSeriesItems(legendData); });
var formatter = this.legend.item.label.formatter;
if (formatter) {
legendData.forEach(function (datum) { return datum.label.text = formatter({
id: datum.id,
itemId: datum.itemId,
value: datum.label.text
}); });
}
this.legend.data = legendData;
};
Object.defineProperty(Chart.prototype, "updatePending", {
get: function () {
return !!this.updateCallbackId;
},
set: function (value) {
var _this = this;
if (this.updateCallbackId) {
clearTimeout(this.updateCallbackId);
this.updateCallbackId = 0;
}
if (value && !this.layoutPending) {
this.updateCallbackId = window.setTimeout(function () {
_this.update();
}, 0);
}
},
enumerable: true,
configurable: true
});
Chart.prototype.update = function () {
this.updatePending = false;
this.series.forEach(function (series) {
if (series.updatePending) {
series.update();
}
});
};
Chart.prototype.positionCaptions = function () {
var _a = this, title = _a.title, subtitle = _a.subtitle;
var titleVisible = false;
var subtitleVisible = false;
var spacing = 10;
var paddingTop = spacing;
if (title && title.enabled) {
title.node.x = this.width / 2;
title.node.y = paddingTop;
titleVisible = true;
var titleBBox = title.node.computeBBox(); // make sure to set node's x/y, then computeBBox
if (titleBBox) {
paddingTop = titleBBox.y + titleBBox.height;
}
if (subtitle && subtitle.enabled) {
subtitle.node.x = this.width / 2;
subtitle.node.y = paddingTop + spacing;
subtitleVisible = true;
var subtitleBBox = subtitle.node.computeBBox();
if (subtitleBBox) {
paddingTop = subtitleBBox.y + subtitleBBox.height;
}
}
}
if (title) {
title.node.visible = titleVisible;
}
if (subtitle) {
subtitle.node.visible = subtitleVisible;
}
this.captionAutoPadding = Math.floor(paddingTop);
};
Chart.prototype.positionLegend = function () {
if (!this.legend.enabled || !this.legend.data.length) {
return;
}
var _a = this, legend = _a.legend, captionAutoPadding = _a.captionAutoPadding, legendAutoPadding = _a.legendAutoPadding;
var width = this.width;
var height = this.height - captionAutoPadding;
var legendGroup = legend.group;
var legendSpacing = legend.spacing;
var translationX = 0;
var translationY = 0;
var legendBBox;
switch (legend.position) {
case 'bottom':
legend.performLayout(width - legendSpacing * 2, 0);
legendBBox = legendGroup.computeBBox();
legendGroup.visible = legendBBox.height < Math.floor((height * 0.5)); // Remove legend if it takes up more than 50% of the chart height.
if (legendGroup.visible) {
translationX = (width - legendBBox.width) / 2 - legendBBox.x;
translationY = captionAutoPadding + height - legendBBox.height - legendBBox.y - legendSpacing;
legendAutoPadding.bottom = legendBBox.height;
}
else {
legendAutoPadding.bottom = 0;
}
break;
case 'top':
legend.performLayout(width - legendSpacing * 2, 0);
legendBBox = legendGroup.computeBBox();
legendGroup.visible = legendBBox.height < Math.floor((height * 0.5));
if (legendGroup.visible) {
translationX = (width - legendBBox.width) / 2 - legendBBox.x;
translationY = captionAutoPadding + legendSpacing - legendBBox.y;
legendAutoPadding.top = legendBBox.height;
}
else {
legendAutoPadding.top = 0;
}
break;
case 'left':
legend.performLayout(0, height - legendSpacing * 2);
legendBBox = legendGroup.computeBBox();
legendGroup.visible = legendBBox.width < Math.floor((width * 0.5)); // Remove legend if it takes up more than 50% of the chart width.
if (legendGroup.visible) {
translationX = legendSpacing - legendBBox.x;
translationY = captionAutoPadding + (height - legendBBox.height) / 2 - legendBBox.y;
legendAutoPadding.left = legendBBox.width;
}
else {
legendAutoPadding.left = 0;
}
break;
default: // case 'right':
legend.performLayout(0, height - legendSpacing * 2);
legendBBox = legendGroup.computeBBox();
legendGroup.visible = legendBBox.width < Math.floor((width * 0.5));
if (legendGroup.visible) {
translationX = width - legendBBox.width - legendBBox.x - legendSpacing;
translationY = captionAutoPadding + (height - legendBBox.height) / 2 - legendBBox.y;
legendAutoPadding.right = legendBBox.width;
}
else {
legendAutoPadding.right = 0;
}
break;
}
if (legendGroup.visible) {
// Round off for pixel grid alignment to work properly.
legendGroup.translationX = Math.floor(translationX + legendGroup.translationX);
legendGroup.translationY = Math.floor(translationY + legendGroup.translationY);
this.legendBBox = legendGroup.computeBBox();
}
};
Chart.prototype.setupDomListeners = function (chartElement) {
chartElement.addEventListener('mousedown', this._onMouseDown);
chartElement.addEventListener('mousemove', this._onMouseMove);
chartElement.addEventListener('mouseup', this._onMouseUp);
chartElement.addEventListener('mouseout', this._onMouseOut);
chartElement.addEventListener('click', this._onClick);
};
Chart.prototype.cleanupDomListeners = function (chartElement) {
chartElement.removeEventListener('mousedown', this._onMouseDown);
chartElement.removeEventListener('mousemove', this._onMouseMove);
chartElement.removeEventListener('mouseup', this._onMouseUp);
chartElement.removeEventListener('mouseout', this._onMouseOut);
chartElement.removeEventListener('click', this._onClick);
};
Chart.prototype.getSeriesRect = function () {
return this.seriesRect;
};
// x/y are local canvas coordinates in CSS pixels, not actual pixels
Chart.prototype.pickSeriesNode = function (x, y) {
if (!(this.seriesRect && this.seriesRect.containsPoint(x, y))) {
return undefined;
}
var allSeries = this.series;
var node = undefined;
for (var i = allSeries.length - 1; i >= 0; i--) {
var series = allSeries[i];
if (!series.visible) {
continue;
}
node = series.pickGroup.pickNode(x, y);
if (node) {
return {
series: series,
node: node
};
}
}
};
// Provided x/y are in canvas coordinates.
Chart.prototype.pickClosestSeriesNodeDatum = function (x, y) {
if (!this.seriesRect || !this.seriesRect.containsPoint(x, y)) {
return undefined;
}
var allSeries = this.series;
function getDistance(p1, p2) {
return Math.sqrt(Math.pow((p1.x - p2.x), 2) + Math.pow((p1.y - p2.y), 2));
}
var minDistance = Infinity;
var closestDatum;
var _loop_1 = function (i) {
var series = allSeries[i];
if (!series.visible) {
return "continue";
}
var hitPoint = series.group.transformPoint(x, y);
series.getNodeData().forEach(function (datum) {
if (!datum.point) {
return;
}
var distance = getDistance(hitPoint, datum.point);
if (distance < minDistance) {
minDistance = distance;
closestDatum = datum;
}
});
};
for (var i = allSeries.length - 1; i >= 0; i--) {
_loop_1(i);
}
return closestDatum;
};
Chart.prototype.onMouseMove = function (event) {
this.handleLegendMouseMove(event);
if (this.tooltip.enabled) {
if (this.tooltip.delay > 0) {
this.tooltip.toggle(false);
}
this.handleTooltip(event);
}
};
Chart.prototype.handleTooltip = function (event) {
var _a = this, lastPick = _a.lastPick, tooltipTracking = _a.tooltip.tracking;
var offsetX = event.offsetX, offsetY = event.offsetY;
var pick = this.pickSeriesNode(offsetX, offsetY);
var nodeDatum;
if (pick && pick.node instanceof shape_1.Shape) {
var node = pick.node;
nodeDatum = node.datum;
if (lastPick && lastPick.datum === nodeDatum) {
lastPick.node = node;
lastPick.event = event;
}
// Marker nodes will have the `point` info in their datums.
// Highlight if not a marker node or, if not in the tracking mode, highlight markers too.
if ((!node.datum.point || !tooltipTracking)) {
if (!lastPick // cursor moved from empty space to a node
|| lastPick.node !== node) { // cursor moved from one node to another
this.onSeriesDatumPick(event, node.datum, node, event);
}
else if (pick.series.tooltip.enabled) { // cursor moved within the same node
this.tooltip.show(event);
}
// A non-marker node (takes precedence over marker nodes) was highlighted.
// Or we are not in the tracking mode.
// Either way, we are done at this point.
return;
}
}
var hideTooltip = false;
// In tracking mode a tooltip is shown for the closest rendered datum.
// This makes it easier to show tooltips when markers are small and/or plentiful
// and also gives the ability to show tooltips even when the series were configured
// to not render markers.
if (tooltipTracking) {
var closestDatum = this.pickClosestSeriesNodeDatum(offsetX, offsetY);
if (closestDatum && closestDatum.point) {
var _b = closestDatum.point, x = _b.x, y = _b.y;
var canvas = this.scene.canvas;
var point = closestDatum.series.group.inverseTransformPoint(x, y);
var canvasRect = canvas.element.getBoundingClientRect();
this.onSeriesDatumPick({
pageX: Math.round(canvasRect.left + window.pageXOffset + point.x),
pageY: Math.round(canvasRect.top + window.pageYOffset + point.y)
}, closestDatum, nodeDatum === closestDatum && pick ? pick.node : undefined, event);
}
else {
hideTooltip = true;
}
}
if (lastPick && (hideTooltip || !tooltipTracking)) {
// Cursor moved from a non-marker node to empty space.
this.dehighlightDatum();
this.tooltip.toggle(false);
this.lastPick = undefined;
}
};
Chart.prototype.onMouseDown = function (event) { };
Chart.prototype.onMouseUp = function (event) { };
Chart.prototype.onMouseOut = function (event) {
this.tooltip.toggle(false);
};
Chart.prototype.onClick = function (event) {
if (this.checkSeriesNodeClick()) {
return;
}
if (this.checkLegendClick(event)) {
return;
}
this.fireEvent({
type: 'click',
event: event
});
};
Chart.prototype.checkSeriesNodeClick = function () {
var lastPick = this.lastPick;
if (lastPick && lastPick.event && lastPick.node) {
var event_1 = lastPick.event, datum = lastPick.datum;
datum.series.fireNodeClickEvent(event_1, datum);
return true;
}
return false;
};
Chart.prototype.onSeriesNodeClick = function (event) {
this.fireEvent(__assign(__assign({}, event), { type: 'seriesNodeClick' }));
};
Chart.prototype.checkLegendClick = function (event) {
var datum = this.legend.getDatumForPoint(event.offsetX, event.offsetY);
if (datum) {
var id_2 = datum.id, itemId = datum.itemId, enabled = datum.enabled;
var series = array_1.find(this.series, function (series) { return series.id === id_2; });
if (series) {
series.toggleSeriesItem(itemId, !enabled);
if (enabled) {
this.tooltip.toggle(false);
}
this.legend.fireEvent({
type: 'click',
event: event,
itemId: itemId,
enabled: !enabled
});
return true;
}
}
return false;
};
Chart.prototype.handleLegendMouseMove = function (event) {
if (!this.legend.enabled) {
return;
}
var offsetX = event.offsetX, offsetY = event.offsetY;
var datum = this.legend.getDatumForPoint(offsetX, offsetY);
var pointerInsideLegend = this.legendBBox.containsPoint(offsetX, offsetY);
if (pointerInsideLegend) {
if (!this.pointerInsideLegend) {
this.pointerInsideLegend = true;
}
}
else if (this.pointerInsideLegend) {
this.pointerInsideLegend = false;
// Dehighlight if the pointer was inside the legend and is now leaving it.
if (this.highlightedDatum) {
this.highlightedDatum = undefined;
this.series.forEach(function (s) { return s.updatePending = true; });
}
return;
}
var oldHighlightedDatum = this.highlightedDatum;
if (datum) {
var id_3 = datum.id, itemId = datum.itemId, enabled = datum.enabled;
if (enabled) {
var series = array_1.find(this.series, function (series) { return series.id === id_3; });
if (series) {
this.highlightedDatum = {
series: series,
itemId: itemId,
datum: undefined
};
}
}
}
// Careful to only schedule updates when necessary.
if ((this.highlightedDatum && !oldHighlightedDatum) ||
(this.highlightedDatum && oldHighlightedDatum &&
(this.highlightedDatum.series !== oldHighlightedDatum.series ||
this.highlightedDatum.itemId !== oldHighlightedDatum.itemId))) {
this.series.forEach(function (s) { return s.updatePending = true; });
}
};
Chart.prototype.onSeriesDatumPick = function (meta, datum, node, event) {
var lastPick = this.lastPick;
if (lastPick) {
if (lastPick.datum === datum) {
return;
}
this.dehighlightDatum();
}
this.lastPick = {
datum: datum,
node: node,
event: event
};
this.highlightDatum(datum);
var html = datum.series.tooltip.enabled && datum.series.getTooltipHtml(datum);
if (html) {
this.tooltip.show(meta, html);
}
};
Chart.prototype.highlightDatum = function (datum) {
this.scene.canvas.element.style.cursor = datum.series.cursor;
this.highlightedDatum = datum;
this.series.forEach(function (s) { return s.updatePending = true; });
};
Chart.prototype.dehighlightDatum = function () {
this.scene.canvas.element.style.cursor = 'default';
this.highlightedDatum = undefined;
this.series.forEach(function (s) { return s.updatePending = true; });
};
Chart.defaultTooltipClass = 'ag-chart-tooltip';
Chart.tooltipDocuments = [];
__decorate([
observable_1.reactive('layoutChange')
], Chart.prototype, "title", void 0);
__decorate([
observable_1.reactive('layoutChange')
], Chart.prototype, "subtitle", void 0);
return Chart;
}(observable_1.Observable));
exports.Chart = Chart;
//# sourceMappingURL=chart.js.map