UNPKG

@amcharts/amcharts5

Version:
740 lines 31.7 kB
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); // `_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