import { DateFormatter, Utils } from 'arm-common'; import p from 'prop-types'; import React, { useLayoutEffect, useMemo, useState } from 'react'; import { ChartContainer, useAm5, useAm5Colors, getRoot } from '../Am5.jsx'; import { AutoSize } from 'arm-core-layouts'; const classNamePrefix = 'armu_ai_chatbot-stacked-chart'; /** * Generates a stacked chart using the provided data and configuration. * * @param {Object} props - The configuration object for the chart. * @param {string} props.id - The unique identifier for the chart. * @param {string} props.xAxis - The field to use for the x-axis. * @param {string} props.yAxis - The field to use for the y-axis. * @param {Array} props.series - The series configuration for the chart. * @param {Array} props.data - The data to be displayed on the chart. * @return {ReactElement} The rendered stacked chart component. */ export default function StackedChart(props) { const { id, xAxis, series, isDateXAxis } = props; const [chartId] = useState(id || Utils.uuid(classNamePrefix)); const am5 = useAm5(); const am5Colors = useAm5Colors(); const data = useMemo(() => { if (isDateXAxis) { return props.data.map((d) => ({...d, [xAxis]: DateFormatter.formatDate(d[xAxis], "DD/MM/YYYY") })) } return props.data; }, [props.data]) useLayoutEffect(() => { if (am5 && data) { const root = getRoot(chartId); const chart = root.container.children.push( am5xy.XYChart.new(root, { layout: root.verticalLayout }) ); const xRenderer = am5xy.AxisRendererX.new(root, {}); const xAxisAm5 = chart.xAxes.push( am5xy.CategoryAxis.new(root, { categoryField: xAxis, renderer: xRenderer, tooltip: am5.Tooltip.new(root, {}) }) ); xRenderer.grid.template.setAll({ location: 1 }) xAxisAm5.data.setAll(data); const yAxisAm5 = chart.yAxes.push( am5xy.ValueAxis.new(root, { min: 0, max: 100, renderer: am5xy.AxisRendererY.new(root, { strokeOpacity: 0.1 }) }) ); chart.set("colors", am5.ColorSet.new(root, { colors: am5Colors })); if (series?.length) { const legend = chart.children.push(am5.Legend.new(root, { centerX: am5.p50, x: am5.p50, })); series.forEach(addSeries.bind({ root, chart, xAxisAm5, yAxisAm5, xAxis, data })); legend.data.setAll(chart.series.values); } } }, [am5, data]); return ( ); } StackedChart.defaultProps = { id: '', xAxis: '', series: [], data: null } StackedChart.propTypes = { id: p.string, xAxis: p.string.isRequired, series: p.arrayOf(p.shape({ name: p.string, yAxis: p.string })).isRequired, data: p.arrayOf(p.object).isRequired } function addSeries(seriesConfig) { const { root, chart, xAxisAm5, yAxisAm5, xAxis, data } = this; const { name, yAxis } = seriesConfig; const series = chart.series.push( am5xy.ColumnSeries.new(root, { name, stacked: true, xAxis: xAxisAm5, yAxis: yAxisAm5, categoryXField: xAxis, valueYField: yAxis }) ); series.columns.template.setAll({ tooltipText: "[bold]{name}[/]: {valueY}", tooltipY: am5.percent(10) }); series.bullets.push(function () { return am5.Bullet.new(root, { sprite: am5.Label.new(root, { text: "{valueY}", fill: root.interfaceColors.get("alternativeText"), centerY: am5.p50, centerX: am5.p50, populateText: true }) }); }); series.data.setAll(data); addRoundedCorners(series, chart, xAxisAm5); series.appear(); } //TODO doesnt work function addRoundedCorners(series, chart, xAxisAm5) { series.columns.template.adapters.add("cornerRadiusTL", function (radius, target) { return isTop.call({ chart, series, xAxisAm5 }, target.dataItem) ? 5 : 0; }); series.columns.template.adapters.add("cornerRadiusTR", function (radius, target) { return isTop.call({ chart, series, xAxisAm5 }, target.dataItem) ? 5 : 0; }); } function getCategoryDataItems(dataItem) { const index = this.xAxisAm5.categoryToIndex(dataItem.get("categoryX")); const items = []; this.chart.series.each((series) => { if (series.get("visible")) { const item = this.series.dataItems[index]; if (item.get("valueY") >= 0) { items.push(item); } else { items.unshift(item); } } }); return items; } function isTop(dataItem) { const items = getCategoryDataItems.call(this, dataItem); if (dataItem.get("valueY") >= 0) { return items.indexOf(dataItem) == (items.length - 1); } else { return items.indexOf(dataItem) == 0; } }