@amcharts/amcharts5
Version:
amCharts 5
741 lines • 31.7 kB
JavaScript
import { __awaiter } from "tslib";
import { XYSeries } from "./XYSeries";
import { Percent } from "../../../core/util/Percent";
import { ColorSet } from "../../../core/util/ColorSet";
import { visualSettings } from "../../../core/render/Graphics";
import * as $array from "../../../core/util/Array";
import * as $type from "../../../core/util/Type";
/**
* Base class for all "column-based" series
*/
export class BaseColumnSeries extends XYSeries {
constructor() {
super(...arguments);
this._ph = 0;
this._pw = 0;
this._columnsUpdated = false;
}
_makeGraphics(listTemplate, dataItem) {
return this.makeColumn(dataItem, listTemplate);
}
_makeFieldNames() {
super._makeFieldNames();
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const categoryAxis = "CategoryAxis";
const valueAxis = "ValueAxis";
if (xAxis.isType(categoryAxis)) {
if (!this.get("openCategoryXField")) {
this._xOpenField = this._xField;
}
}
if (xAxis.isType(valueAxis)) {
if (!this.get("openValueXField")) {
this._xOpenField = this._xField;
}
}
if (yAxis.isType(categoryAxis)) {
if (!this.get("openCategoryYField")) {
this._yOpenField = this._yField;
}
}
if (yAxis.isType(valueAxis)) {
if (!this.get("openValueYField")) {
this._yOpenField = this._yField;
}
}
}
_prepareChildren() {
super._prepareChildren();
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const len = this.dataItems.length;
const startIndex = Math.max(0, this.startIndex() - 2);
const endIndex = Math.min(this.endIndex() + 2, len - 1);
if (xAxis.inited && yAxis.inited) {
for (let i = startIndex; i <= endIndex; i++) {
let dataItem = this.dataItems[i];
this._createGraphics(dataItem);
}
}
}
_updateChildren() {
const chart = this.chart;
if (chart) {
this._ph = chart.plotContainer.height();
this._pw = chart.plotContainer.width();
}
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const baseAxis = this.get("baseAxis");
this._colorColumns();
const columnsTemplate = this.columns.template;
// A series colour on the template would shadow the per-column one
const perColumn = this.get("colorByDataItem", false);
if (this.isDirty("fill") && !perColumn) {
if (columnsTemplate.get("fill") == null) {
columnsTemplate._setC("fill", this.get("fill"));
}
}
if (this.isDirty("fillPattern")) {
if (columnsTemplate.get("fillPattern") == null) {
columnsTemplate._setC("fillPattern", this.get("fillPattern"));
}
}
if (this.isDirty("stroke") && !perColumn) {
if (columnsTemplate.get("stroke") == null) {
columnsTemplate._setC("stroke", this.get("stroke"));
}
}
// Clustering is computed from all column series sharing the base axis, so
// toggling `clustered` OR `stacked` on one series changes the slot count
// for the others too (a stacked series collapses into a single slot).
// Reposition this series and mark its siblings dirty so they reflow.
if (this.isDirty("clustered") || this.isDirty("stacked")) {
this._valuesDirty = true;
if (baseAxis) {
$array.each(baseAxis.series, (series) => {
if (series instanceof BaseColumnSeries && series !== this) {
series.markDirtyValues();
}
});
}
}
let index = 0;
let clusterCount = 0;
let i = 0;
if (baseAxis) {
$array.each(baseAxis.series, (series) => {
if (series instanceof BaseColumnSeries) {
const stacked = series.get("stacked");
if (stacked && i == 0) {
clusterCount++;
}
if (!stacked && series.get("clustered")) {
clusterCount++;
}
}
if (series === this) {
index = clusterCount - 1;
}
i++;
});
}
if (!this.get("clustered")) {
index = 0;
clusterCount = 1;
}
if (clusterCount === 0) {
clusterCount = 1;
index = 0;
}
const cellStartLocation = "cellStartLocation";
const cellEndLocation = "cellEndLocation";
const cellLocationX0 = this._xRenderer.get(cellStartLocation, 0);
const cellLocationX1 = this._xRenderer.get(cellEndLocation, 1);
const cellLocationY0 = this._yRenderer.get(cellStartLocation, 0);
const cellLocationY1 = this._yRenderer.get(cellEndLocation, 1);
this._aLocationX0 = cellLocationX0 + (index / clusterCount) * (cellLocationX1 - cellLocationX0);
this._aLocationX1 = cellLocationX0 + (index + 1) / clusterCount * (cellLocationX1 - cellLocationX0);
;
this._aLocationY0 = cellLocationY0 + (index / clusterCount) * (cellLocationY1 - cellLocationY0);
this._aLocationY1 = cellLocationY0 + (index + 1) / clusterCount * (cellLocationY1 - cellLocationY0);
if (xAxis.inited && yAxis.inited) {
if (this._axesDirty || this._valuesDirty || this._stackDirty || this.isDirty("vcx") || this.isDirty("vcy") || this._sizeDirty) {
const len = this.dataItems.length;
let startIndex = Math.max(0, this.startIndex() - 2);
let endIndex = Math.min(this.endIndex() + 2, len - 1);
for (let i = 0; i < startIndex; i++) {
this._toggleColumn(this.dataItems[i], false);
}
let previous = this.dataItems[startIndex];
for (let i = startIndex; i <= endIndex; i++) {
let dataItem = this.dataItems[i];
if (dataItem.get("valueX") != null && dataItem.get("valueY") != null) {
previous = dataItem;
if (i > 0 && startIndex > 0) {
for (let j = i - 1; j >= 0; j--) {
let dataItem = this.dataItems[j];
if (dataItem.get("valueX") != null && dataItem.get("valueY") != null) {
previous = dataItem;
break;
}
}
}
break;
}
else {
this._toggleColumn(dataItem, false);
}
}
this._beforeColumnsDraw();
let axisCase = 0;
if (yAxis.isType("CategoryAxis") && xAxis.isType("CategoryAxis")) {
axisCase = 2;
}
else if (xAxis === baseAxis) {
axisCase = 0;
}
else if (yAxis === baseAxis) {
axisCase = 1;
}
for (let i = startIndex; i <= endIndex; i++) {
let dataItem = this.dataItems[i];
this._updateGraphics(dataItem, previous, axisCase);
if (dataItem.get("valueX") != null && dataItem.get("valueY") != null) {
previous = dataItem;
}
}
this._afterColumnsDraw();
for (let i = endIndex + 1; i < len; i++) {
this._toggleColumn(this.dataItems[i], false);
}
this._columnsUpdated = true;
}
}
else {
this._skipped = true;
}
if (!this.getPrivate("doNotUpdateLegend")) {
this.updateLegendMarker(this.get("tooltipDataItem"));
}
super._updateChildren();
}
_afterChanged() {
super._afterChanged();
this._columnsUpdated = false;
}
_beforeColumnsDraw() {
}
_afterColumnsDraw() {
}
/**
* The palette each column is colored from. Made on demand so every series
* gets its own - a themed one would be a single instance shared by all of
* them.
*
* @ignore
*/
_afterNew() {
super._afterNew();
// Made up front rather than when the first column asks for a color: a
// palette that only appears on the next frame is not there to be read by
// anything looking at the series right after the setting is switched on.
// One per series, so editing one series' palette leaves the others alone.
if (!this.get("colors")) {
// the series made it, nobody asked for it
this._setC("colors", ColorSet.new(this._root, {}));
}
}
_colorSet() {
return this.get("colors");
}
/**
* Gives every column its own color, by data item index.
*
* By index rather than by stepping the palette, so the colors do not depend
* on how many times the data has been set, survive a theme being applied
* again, and are there for data items the axis groups together.
*
* @ignore
*/
_colorColumns() {
const template = this.columns.template;
if (!this.get("colorByDataItem")) {
// Switched off again: hand back what the per-column coloring took over,
// or the columns keep the colors it gave them
if (this.isDirty("colorByDataItem")) {
$array.each(this.dataItems, (dataItem) => {
dataItem.setRaw("fill", undefined);
const graphics = dataItem.get("graphics");
if (graphics) {
graphics.remove("fill");
graphics.remove("stroke");
}
});
// and put the series color back on the template, which is where the
// columns take it from - the usual pass below only runs when the
// series color itself changes, and it has not
const fill = this.get("fill");
if (fill != null && template.get("fill") == null) {
template._setC("fill", fill);
}
const stroke = this.get("stroke");
if (stroke != null && template.get("stroke") == null) {
template._setC("stroke", stroke);
}
}
return;
}
// Switched on after the chart was built, the series colour is already sitting
// on the column template, where it shadows everything below. Take back what
// the library put there - a fill somebody asked for is left alone
if (template._computedSettings["fill"]) {
template.remove("fill");
}
if (template._computedSettings["stroke"]) {
template.remove("stroke");
}
const colors = this._colorSet();
const step = colors.get("step", 1);
const startIndex = colors.get("startIndex", 0);
$array.each(this.dataItems, (dataItem, index) => {
const color = colors.getIndex(startIndex + index * step);
dataItem.setRaw("fill", color);
const graphics = dataItem.get("graphics");
if (graphics) {
// a default, so a column template or a heat rule still wins, and it
// is re-applied here because applying themes again drops it
graphics._setDefault("fill", color);
graphics._setDefault("stroke", color);
}
});
}
_createGraphics(dataItem) {
let graphics = dataItem.get("graphics");
if (!graphics) {
graphics = this._makeGraphics(this.columns, dataItem);
dataItem.set("graphics", graphics);
graphics._setDataItem(dataItem);
this._linkSprite(graphics, dataItem);
// `_colorColumns` runs earlier in the same pass, so the color is already
// on the data item - the column just did not exist yet to take it
const fill = dataItem.get("fill");
if (fill != null) {
graphics._setDefault("fill", fill);
graphics._setDefault("stroke", fill);
}
const legendDataItem = dataItem.get("legendDataItem");
if (legendDataItem) {
const markerRectangle = legendDataItem.get("markerRectangle");
if (markerRectangle) {
const ds = markerRectangle.states.lookup("default");
$array.each(visualSettings, (setting) => {
const value = graphics.get(setting, this.get(setting));
markerRectangle.set(setting, value);
ds.set(setting, value);
});
}
}
let graphicsArray = dataItem.get("rangeGraphics");
if (graphicsArray) {
$array.each(graphicsArray, (graphics) => {
graphics.dispose();
});
}
graphicsArray = [];
dataItem.setRaw("rangeGraphics", graphicsArray);
this.axisRanges.each((axisRange) => {
const container = axisRange.container;
const rangeGraphics = this._makeGraphics(axisRange.columns, dataItem);
if (graphicsArray) {
graphicsArray.push(rangeGraphics);
}
rangeGraphics.setPrivate("list", axisRange.columns);
container.children.push(rangeGraphics);
});
}
}
createAxisRange(axisDataItem) {
$array.each(this.dataItems, (dataItem) => {
const graphics = dataItem.get("graphics");
if (graphics) {
graphics.dispose();
dataItem.set("graphics", undefined);
}
});
return super.createAxisRange(axisDataItem);
}
_updateGraphics(dataItem, previousDataItem, axisCase) {
let graphics = dataItem.get("graphics");
//if (!graphics) {
// this._createGraphics(dataItem);
// graphics = dataItem.get("graphics")!;
//}
const xField = this._xField;
const yField = this._yField;
const valueX = dataItem.get(xField);
const valueY = dataItem.get(yField);
const exactLocationX = this.get("exactLocationX", false);
//const exactLocationY = this.get("exactLocationY", false);
if (valueX != null && valueY != null) {
const xOpenField = this._xOpenField;
const yOpenField = this._yOpenField;
const locationX = this.get("locationX", dataItem.get("locationX", 0.5));
const locationY = this.get("locationY", dataItem.get("locationY", 0.5));
const openLocationX = this.get("openLocationX", dataItem.get("openLocationX", locationX));
const openLocationY = this.get("openLocationY", dataItem.get("openLocationY", locationY));
const width = graphics.get("width");
const height = graphics.get("height");
const stacked = this.get("stacked");
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const xStart = xAxis.get("start");
const xEnd = xAxis.get("end");
const yStart = yAxis.get("start");
const yEnd = yAxis.get("end");
let l;
let r;
let t;
let b;
let vcy = this.get("vcy", 1);
let vcx = this.get("vcx", 1);
let fitW = false;
let fitH = false;
if (axisCase == 0) {
let startLocation = this._aLocationX0 + openLocationX - 0.5;
let endLocation = this._aLocationX1 + locationX - 0.5;
if (width instanceof Percent) {
let offset = (endLocation - startLocation) * (1 - width.value) / 2;
startLocation += offset;
endLocation -= offset;
}
l = xAxis.getDataItemPositionX(dataItem, xOpenField, startLocation, vcx, exactLocationX);
r = xAxis.getDataItemPositionX(dataItem, xField, endLocation, vcx, exactLocationX);
t = yAxis.getDataItemPositionY(dataItem, yField, locationY, vcy);
if (this._yOpenField !== this._yField) {
b = yAxis.getDataItemPositionY(dataItem, yOpenField, openLocationY, vcy);
}
else {
if (stacked) {
let stackToItemY = dataItem.get("stackToItemY");
if (stackToItemY) {
b = yAxis.getDataItemPositionY(stackToItemY, yField, openLocationY, stackToItemY.component.get("vcy"));
}
else {
b = yAxis.basePosition();
}
}
else {
b = yAxis.basePosition();
}
}
dataItem.setRaw("point", { x: l + (r - l) / 2, y: t });
fitH = true;
}
else if (axisCase == 1) {
let startLocation = this._aLocationY0 + openLocationY - 0.5;
let endLocation = this._aLocationY1 + locationY - 0.5;
if (height instanceof Percent) {
let offset = (endLocation - startLocation) * (1 - height.value) / 2;
startLocation += offset;
endLocation -= offset;
}
t = yAxis.getDataItemPositionY(dataItem, yOpenField, startLocation, vcy);
b = yAxis.getDataItemPositionY(dataItem, yField, endLocation, vcy);
r = xAxis.getDataItemPositionX(dataItem, xField, locationX, vcx, exactLocationX);
if (this._xOpenField !== this._xField) {
l = xAxis.getDataItemPositionX(dataItem, xOpenField, openLocationX, vcx, exactLocationX);
}
else {
if (stacked) {
let stackToItemX = dataItem.get("stackToItemX");
if (stackToItemX) {
l = xAxis.getDataItemPositionX(stackToItemX, xField, openLocationX, stackToItemX.component.get("vcx"), exactLocationX);
}
else {
l = xAxis.basePosition();
}
}
else {
l = xAxis.basePosition();
}
}
fitW = true;
dataItem.setRaw("point", { x: r, y: t + (b - t) / 2 });
}
else if (axisCase == 2) {
let startLocation = this._aLocationX0 + openLocationX - 0.5;
let endLocation = this._aLocationX1 + locationX - 0.5;
if (width instanceof Percent) {
let offset = (endLocation - startLocation) * (1 - width.value) / 2;
startLocation += offset;
endLocation -= offset;
}
l = xAxis.getDataItemPositionX(dataItem, xOpenField, startLocation, vcx);
r = xAxis.getDataItemPositionX(dataItem, xField, endLocation, vcx);
startLocation = this._aLocationY0 + openLocationY - 0.5;
endLocation = this._aLocationY1 + locationY - 0.5;
if (height instanceof Percent) {
let offset = (endLocation - startLocation) * (1 - height.value) / 2;
startLocation += offset;
endLocation -= offset;
}
t = yAxis.getDataItemPositionY(dataItem, yOpenField, startLocation, vcy);
b = yAxis.getDataItemPositionY(dataItem, yField, endLocation, vcy);
dataItem.setRaw("point", { x: l + (r - l) / 2, y: t + (b - t) / 2 });
}
this._applyGraphicsStates(dataItem, previousDataItem);
this._updateSeriesGraphics(dataItem, graphics, l, r, t, b, fitW, fitH);
if ((l < xStart && r < xStart) || (l > xEnd && r > xEnd) || (t < yStart && b <= yStart) || (t >= yEnd && b > yEnd) || $type.isNaN(l) || $type.isNaN(t)) {
this._toggleColumn(dataItem, false);
}
else {
this._toggleColumn(dataItem, true);
}
let rangeGraphics = dataItem.get("rangeGraphics");
if (rangeGraphics) {
$array.each(rangeGraphics, (graphics) => {
this._updateSeriesGraphics(dataItem, graphics, l, r, t, b, fitW, fitH);
});
}
}
else {
this._toggleColumn(dataItem, false);
}
}
_updateSeriesGraphics(dataItem, graphics, l, r, t, b, fitW, fitH) {
const width = graphics.get("width");
const height = graphics.get("height");
const maxWidth = graphics.get("maxWidth");
const maxHeight = graphics.get("maxHeight");
const ptl = this.getPoint(l, t);
const pbr = this.getPoint(r, b);
const tooltipPoint = dataItem.get("point");
if (tooltipPoint) {
const point = this.getPoint(tooltipPoint.x, tooltipPoint.y);
tooltipPoint.x = point.x + this._x;
tooltipPoint.y = point.y + this._y;
}
l = ptl.x;
r = pbr.x;
t = ptl.y;
b = pbr.y;
if ($type.isNumber(width)) {
const offset = ((r - l) - width) / 2;
l += offset;
r -= offset;
}
if ($type.isNumber(maxWidth) && maxWidth < Math.abs(r - l)) {
const offset = ((r - l) - maxWidth) / 2;
l += offset;
r -= offset;
}
if ($type.isNumber(height)) {
const offset = ((b - t) - height) / 2;
t += offset;
b -= offset;
}
if ($type.isNumber(maxHeight) && maxHeight < Math.abs(b - t)) {
const offset = ((b - t) - maxHeight) / 2;
t += offset;
b -= offset;
}
if (this.get("adjustBulletPosition")) {
if (fitW) {
r = Math.min(Math.max(0, r), this._pw);
l = Math.min(Math.max(0, l), this._pw);
}
if (fitH) {
t = Math.min(Math.max(0, t), this._ph);
b = Math.min(Math.max(0, b), this._ph);
}
}
dataItem.setRaw("left", l);
dataItem.setRaw("right", r);
dataItem.setRaw("top", t);
dataItem.setRaw("bottom", b);
graphics.setPrivate("width", r - l);
graphics.setPrivate("height", b - t);
graphics.set("x", l);
graphics.set("y", b - (b - t));
}
_handleDataSetChange() {
super._handleDataSetChange();
$array.each(this._dataItems, (dataItem) => {
this._toggleColumn(dataItem, false);
});
}
_applyGraphicsStates(dataItem, previousDataItem) {
const graphics = dataItem.get("graphics");
const dropFromOpen = graphics.states.lookup("dropFromOpen");
const riseFromOpen = graphics.states.lookup("riseFromOpen");
const dropFromPrevious = graphics.states.lookup("dropFromPrevious");
const riseFromPrevious = graphics.states.lookup("riseFromPrevious");
if (dropFromOpen || dropFromPrevious || riseFromOpen || riseFromPrevious) {
const xAxis = this.get("xAxis");
const yAxis = this.get("yAxis");
const baseAxis = this.get("baseAxis");
let open;
let close;
let previousClose;
if (baseAxis === xAxis && yAxis.isType("ValueAxis")) {
open = dataItem.get(this._yOpenField);
close = dataItem.get(this._yField);
previousClose = previousDataItem.get(this._yField);
}
else if (baseAxis === yAxis && xAxis.isType("ValueAxis")) {
open = dataItem.get(this._xOpenField);
close = dataItem.get(this._xField);
previousClose = previousDataItem.get(this._xField);
}
if ($type.isNumber(open) && $type.isNumber(close)) {
if (close < open) {
if (dropFromOpen) {
dropFromOpen.apply();
}
}
else {
if (riseFromOpen) {
riseFromOpen.apply();
}
}
if ($type.isNumber(previousClose)) {
if (close < previousClose) {
if (dropFromPrevious) {
dropFromPrevious.apply();
}
}
else {
if (riseFromPrevious) {
riseFromPrevious.apply();
}
}
}
}
}
}
/**
* @ignore
*/
disposeDataItem(dataItem) {
super.disposeDataItem(dataItem);
const graphics = dataItem.get("graphics");
if (graphics) {
this.columns.removeValue(graphics);
graphics.dispose();
}
const rangeGraphics = dataItem.get("rangeGraphics");
if (rangeGraphics) {
$array.each(rangeGraphics, (graphics) => {
const list = graphics.getPrivate("list");
if (list) {
list.removeValue(graphics);
}
graphics.dispose();
});
}
}
/**
* Hides series's data item.
*
* @param dataItem Data item
* @param duration Animation duration in milliseconds
* @return Promise
*/
hideDataItem(dataItem, duration) {
const _super = Object.create(null, {
hideDataItem: { get: () => super.hideDataItem }
});
return __awaiter(this, void 0, void 0, function* () {
const promises = [_super.hideDataItem.call(this, dataItem, duration)];
const graphics = dataItem.get("graphics");
if (graphics) {
promises.push(graphics.hide(duration));
}
const rangeGraphics = dataItem.get("rangeGraphics");
if (rangeGraphics) {
$array.each(rangeGraphics, (graphics) => {
promises.push(graphics.hide(duration));
});
}
yield Promise.all(promises);
});
}
_toggleColumn(dataItem, visible) {
const graphics = dataItem.get("graphics");
if (graphics) {
graphics.setPrivate("visible", visible);
}
const rangeGraphics = dataItem.get("rangeGraphics");
if (rangeGraphics) {
$array.each(rangeGraphics, (graphics) => {
graphics.setPrivate("visible", visible);
});
}
const bullets = dataItem.bullets;
if (bullets) {
$array.each(bullets, (bullet) => {
bullet.setPrivate("hidden", !visible);
});
}
}
/**
* Shows series's data item.
*
* @param dataItem Data item
* @param duration Animation duration in milliseconds
* @return Promise
*/
showDataItem(dataItem, duration) {
const _super = Object.create(null, {
showDataItem: { get: () => super.showDataItem }
});
return __awaiter(this, void 0, void 0, function* () {
const promises = [_super.showDataItem.call(this, dataItem, duration)];
const graphics = dataItem.get("graphics");
if (graphics) {
promises.push(graphics.show(duration));
}
const rangeGraphics = dataItem.get("rangeGraphics");
if (rangeGraphics) {
$array.each(rangeGraphics, (graphics) => {
promises.push(graphics.show(duration));
});
}
yield Promise.all(promises);
});
}
/**
* @ignore
*/
updateLegendMarker(dataItem) {
let legendDataItem = this.get("legendDataItem");
if (this.get("useLastColorForLegendMarker")) {
if (!dataItem) {
const lastDataItem = this.dataItems[this.endIndex() - 1];
if (lastDataItem) {
dataItem = lastDataItem;
}
}
}
// Colored per data item, so the series has no color of its own for the
// marker to fall back on - use the first column's
if (!dataItem && this.get("colorByDataItem")) {
dataItem = this.dataItems[0];
}
if (legendDataItem) {
let graphics = this.columns.template;
if (dataItem) {
let column = dataItem.get("graphics");
if (column) {
graphics = column;
}
}
const markerRectangle = legendDataItem.get("markerRectangle");
if (markerRectangle) {
if (!legendDataItem.get("itemContainer").get("disabled")) {
const ds = markerRectangle.states.lookup("default");
$array.each(visualSettings, (setting) => {
const value = graphics.get(setting, this.get(setting));
markerRectangle.set(setting, value);
ds.set(setting, value);
});
}
}
}
}
_getTooltipTarget(dataItem) {
if (this.get("seriesTooltipTarget") == "bullet") {
return super._getTooltipTarget(dataItem);
}
let column = dataItem.get("graphics");
if (column) {
return column;
}
return this;
}
}
BaseColumnSeries.className = "BaseColumnSeries";
BaseColumnSeries.classNames = XYSeries.classNames.concat([BaseColumnSeries.className]);
//# sourceMappingURL=BaseColumnSeries.js.map