import { Component, NgZone, ElementRef, Input, OnChanges, AfterViewInit } from "@angular/core"; import { Portlet } from "../../portlet/portlet"; import { BaseAmChart } from "./baseAmChart"; import * as am4core from "@amcharts/amcharts4/core"; import * as am4charts from "@amcharts/amcharts4/charts"; import am4themes_dataviz from "@amcharts/amcharts4/themes/dataviz"; export type SerieItemTypes = "line" | "bar" | "bar3D" | "area" | "cylinder" | "candlestick"; export interface IXYChartSerieItem { name: string; valueField: string; type: SerieItemTypes; axis?: string; hidden?: boolean; stacked?: boolean; color?: string; // #hex tooltip?: string; } export interface IXYChartAxisItem { name: string; title: string; opposite?: boolean; } @Component({ selector: "rd-amchart-xy", template: `
` }) export class AmChartXY extends BaseAmChart implements OnChanges, AfterViewInit { constructor(zone: NgZone, public element: ElementRef, portlet: Portlet) { super(zone, element, portlet); } @Input("rd-data") data: Array = []; @Input("rd-series") series: Array = []; @Input("rd-axis") axis: Array = [{ name: "default", title: "", opposite: false }]; @Input("rd-key-field") keyField: string; @Input("rd-cursor") cursor: boolean = true; @Input("rd-legend-enabled") legendEnabled: boolean = true; @Input("rd-show-bullet") showBullet: boolean = false; @Input("rd-argumentAxis-rotate") argumentAxisRotate: boolean = true; @Input("rd-custom-palette") customPalette: boolean = true; chart: am4charts.XYChart; categoryAxis: am4charts.CategoryAxis; valueAxises = {}; ngAfterViewInit() { this.zone.runOutsideAngular(() => { if (this.customPalette) am4core.useTheme(this.custom_am4theme); else am4core.useTheme(am4themes_dataviz); this.chart = am4core.create(this.container, this.dimension == "2D" ? am4charts.XYChart : am4charts.XYChart3D); this.chart.data = this.data; this.chart.titles.create().text = this.title; this.categoryAxis = this.chart.xAxes.push(new am4charts.CategoryAxis()); this.categoryAxis.dataFields.category = this.keyField; this.categoryAxis.renderer.minGridDistance = 40; if (this.argumentAxisRotate) this.categoryAxis.renderer.labels.template.rotation = -90; this.setAxis(); this.setSeries(); if (this.cursor) this.chart.cursor = new am4charts.XYCursor(); if (this.export) { this.chart.exporting.menu = new am4core.ExportMenu(); this.setExportMenu(); } if (this.legendEnabled) { this.chart.legend = new am4charts.Legend(); this.chart.legend.itemContainers.template.events.on("over", (e) => { this.chart.series.values.filter((serieItem: am4charts.ColumnSeries) => { if (serieItem.name == e.target.dataItem["name"]) { if (serieItem.className == "LineSeries") { serieItem.strokeWidth = 4; serieItem.strokeOpacity = 0.5; } else serieItem.columns.template.fillOpacity = .6 // ColumnSeries } }); }); this.chart.legend.itemContainers.template.events.on("out", (e) => { this.chart.series.values.filter((serieItem: am4charts.ColumnSeries) => { if (serieItem.name == e.target.dataItem["name"]) { if (serieItem.className == "LineSeries") { serieItem.strokeWidth = 2; serieItem.strokeOpacity = 1; } else serieItem.columns.template.fillOpacity = 1 // ColumnSeries } }); }); } }); } ngOnChanges(changes) { if (!this.chart) return; this.chart.colors.currentStep = 0; if (changes.data) this.chart.data = this.data; if (changes.series) this.setSeries(); if (changes.title) { this.chart.titles.clear(); this.chart.titles.create().text = this.title; } } setAxis() { for (let item of this.axis) { let valueAxis = this.chart.yAxes.push(new am4charts.ValueAxis()); valueAxis.title.text = item.title; valueAxis.title.fontWeight = "bold"; valueAxis.renderer.opposite = item.opposite || false; this.valueAxises[item.name] = valueAxis; } } setSeries() { this.chart.series.clear(); for (let item of this.series || []) { let serie; switch (item.type) { case "line": case "area": serie = this.chart.series.push(new am4charts.LineSeries()); serie.strokeWidth = 2; serie.tensionX = .8; serie.connect = false; if (item.type == "area") serie.fillOpacity = .5; if (this.showBullet) serie.bullets.push(new am4charts.CircleBullet()); break; case "bar": serie = this.chart.series.push(new am4charts.ColumnSeries()); if (!item.stacked) { serie.columns.template.column.cornerRadiusTopLeft = 10; serie.columns.template.column.cornerRadiusTopRight = 10; serie.columns.template.column.fillOpacity = 0.8; } break; case "bar3D": serie = this.chart.series.push(new am4charts.ColumnSeries3D()); serie.columns.template.fillOpacity = .8; serie.columns.template.width = am4core.percent(50); this.categoryAxis.renderer.minGridDistance = 50; this.categoryAxis.renderer.grid.template.location = 0; this.categoryAxis.renderer.cellStartLocation = .1; this.categoryAxis.renderer.cellEndLocation = .9; break; case "cylinder": // dimension must 3d this.chart.paddingBottom = 30; (this.chart).angle = 35; this.categoryAxis.renderer.inside = true; let labelTemplate = this.categoryAxis.renderer.labels.template; labelTemplate.rotation = -90; labelTemplate.horizontalCenter = "left"; labelTemplate.verticalCenter = "middle"; labelTemplate.dy = 10; labelTemplate.inside = false; serie = this.chart.series.push(new am4charts.ConeSeries()); let columnTemplate = serie.columns.template; columnTemplate.adapter.add("fill", (fill, target) => { return this.chart.colors.getIndex(target.dataItem.index); }); columnTemplate.adapter.add("stroke", (stroke, target) => { return this.chart.colors.getIndex(target.dataItem.index); }); break; case "candlestick": serie = this.chart.series.push(new am4charts.CandlestickSeries()); serie.dataFields.valueY = "close"; serie.dataFields.openValueY = "open"; serie.dataFields.lowValueY = "low"; serie.dataFields.highValueY = "high"; serie.simplifiedProcessing = true; serie.tooltipText = item.tooltip || "Open:${openValueY.value}\nLow:${lowValueY.value}\nHigh:${highValueY.value}\nClose:${valueY.value}"; break; } serie.name = item.name; serie.hidden = !!item.hidden; serie.stacked = !!item.stacked; serie.dataFields.categoryX = this.keyField; serie.dataFields.valueY = item.valueField; if (!serie.tooltipText) serie.tooltipText = "{name}: [bold]{valueY}[/]"; if (item.axis) serie.yAxis = this.valueAxises[item.axis]; if (item.color) { if (item.type == "line" || item.type == "area") { serie.stroke = am4core.color(item.color); } else { serie.columns.template.stroke = am4core.color(item.color); serie.columns.template.fill = am4core.color(item.color); } serie.tooltip.getFillFromObject = false; serie.tooltip.background.fill = am4core.color(item.color); } } } }