182 lines
5.3 KiB
JavaScript

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 (
<ChartContainer id={chartId} className={classNamePrefix} />
);
}
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;
}
}