184 lines
5.3 KiB
JavaScript
184 lines
5.3 KiB
JavaScript
import { 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-bar-chart';
|
|
|
|
export default function BarChart(props) {
|
|
const { id, xAxis, yAxis, series, data } = props;
|
|
|
|
const [chartId] = useState(id || Utils.uuid(classNamePrefix));
|
|
const am5 = useAm5();
|
|
const am5Colors = useAm5Colors();
|
|
const allValues = useMemo(() => {
|
|
if (data?.length) {
|
|
if (series?.length) {
|
|
const values = [];
|
|
for (let item of data) {
|
|
for (let serie of series) {
|
|
if (!isNaN(item[serie.yAxis])) {
|
|
values.push(item[serie.yAxis]);
|
|
}
|
|
}
|
|
}
|
|
return values;
|
|
} else {
|
|
return data.map(d => d[yAxis]);
|
|
}
|
|
}
|
|
return [];
|
|
}, [data, series, yAxis]);
|
|
|
|
const minValue = Math.min(...allValues);
|
|
|
|
useLayoutEffect(() => {
|
|
if (am5 && data) {
|
|
const root = getRoot(chartId);
|
|
|
|
const chart = root.container.children.push(
|
|
am5xy.XYChart.new(root, {
|
|
layout: root.verticalLayout
|
|
})
|
|
);
|
|
|
|
const yAxisAm5 = chart.yAxes.push(
|
|
am5xy.CategoryAxis.new(root, {
|
|
categoryField: xAxis,
|
|
renderer: am5xy.AxisRendererY.new(root, {
|
|
inversed: true,
|
|
cellStartLocation: 0.1,
|
|
cellEndLocation: 0.9
|
|
})
|
|
})
|
|
);
|
|
|
|
yAxisAm5.data.setAll(data);
|
|
|
|
const xAxisAm5 = chart.xAxes.push(
|
|
am5xy.ValueAxis.new(root, {
|
|
renderer: am5xy.AxisRendererX.new(root, {
|
|
strokeOpacity: 0.1
|
|
}),
|
|
min: minValue < 0 ? minValue : 0,
|
|
})
|
|
);
|
|
|
|
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);
|
|
} else if (yAxis){
|
|
addSeries.call({ root, chart, xAxisAm5, yAxisAm5, xAxis, data }, { yAxis });
|
|
}
|
|
|
|
const cursor = chart.set("cursor", am5xy.XYCursor.new(root, {
|
|
behavior: "zoomY"
|
|
}));
|
|
cursor.lineY.set("forceHidden", true);
|
|
cursor.lineX.set("forceHidden", true);
|
|
}
|
|
}, [am5, allValues]);
|
|
|
|
const height = useMemo(() => calcHeight(data, series), [data, series]);
|
|
|
|
return (
|
|
<ChartContainer id={chartId} style={{ height }} className={classNamePrefix} />
|
|
);
|
|
}
|
|
|
|
BarChart.defaultProps = {
|
|
id: '',
|
|
xAxis: '',
|
|
yAxis: '',
|
|
series: [],
|
|
data: null
|
|
}
|
|
|
|
BarChart.propTypes = {
|
|
id: p.string,
|
|
xAxis: p.string.isRequired,
|
|
yAxis: p.string,
|
|
series: p.arrayOf(p.shape({
|
|
name: p.string,
|
|
yAxis: p.string
|
|
})),
|
|
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,
|
|
minBulletDistance: 10,
|
|
xAxis: xAxisAm5,
|
|
yAxis: yAxisAm5,
|
|
valueXField: yAxis,
|
|
categoryYField: xAxis,
|
|
sequencedInterpolation: true,
|
|
tooltip: am5.Tooltip.new(root, {
|
|
pointerOrientation: "horizontal",
|
|
labelText: "[bold]{name}[/]\n{categoryY}: {valueX}"
|
|
})
|
|
})
|
|
);
|
|
|
|
series.columns.template.setAll({
|
|
height: am5.p100,
|
|
strokeOpacity: 0
|
|
});
|
|
|
|
|
|
series.bullets.push(function () {
|
|
return am5.Bullet.new(root, {
|
|
locationX: 1,
|
|
locationY: 0.5,
|
|
sprite: am5.Label.new(root, {
|
|
centerY: am5.p50,
|
|
text: "{valueX}",
|
|
populateText: true
|
|
})
|
|
});
|
|
});
|
|
|
|
series.bullets.push(function () {
|
|
return am5.Bullet.new(root, {
|
|
locationX: 1,
|
|
locationY: 0.5,
|
|
sprite: am5.Label.new(root, {
|
|
centerX: am5.p100,
|
|
centerY: am5.p50,
|
|
text: "{name}",
|
|
fill: am5.color(0xffffff),
|
|
populateText: true
|
|
})
|
|
});
|
|
});
|
|
|
|
series.data.setAll(data);
|
|
series.columns.template.setAll({ cornerRadiusBR: 5, cornerRadiusTR: 5, strokeOpacity: 0 });
|
|
series.appear();
|
|
}
|
|
|
|
function calcHeight(data, series) {
|
|
const lineHeight = AutoSize.convertRemToPixels(3);
|
|
const barHeight = AutoSize.convertRemToPixels(4);
|
|
|
|
return (lineHeight * (data.length + 1)) + (barHeight * ((series?.length || 0) + 1)) + AutoSize.convertRemToPixels(5);
|
|
} |