ghost-portal/packages/react-components/src/Chart/HorizBar.tsx
2021-01-03 11:33:39 +01:00

110 lines
3.1 KiB
TypeScript

// Copyright 2017-2021 @polkadot/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { HorizBarProps, HorizBarValue } from './types';
import ChartJs from 'chart.js';
import React, { useEffect, useState } from 'react';
import { HorizontalBar } from 'react-chartjs-2';
import { bnToBn, isNumber } from '@polkadot/util';
interface State {
chartData?: ChartJs.ChartData;
chartOptions?: ChartJs.ChartOptions;
jsonValues?: string;
}
interface TooltipItem {
index: number;
}
interface Config {
labels: string[];
datasets: {
data: number[];
backgroundColor: string[];
hoverBackgroundColor: string[];
}[];
}
const alphaColor = (hexColor: string): string =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
ChartJs.helpers.color(hexColor).alpha(0.65).rgbString();
function calculateOptions (aspectRatio: number, values: HorizBarValue[], jsonValues: string, max: number, showLabels: boolean): State {
const chartData = values.reduce((data, { colors: [normalColor = '#00f', hoverColor], label, value }): Config => {
const dataset = data.datasets[0];
dataset.backgroundColor.push(alphaColor(normalColor));
dataset.hoverBackgroundColor.push(alphaColor(hoverColor || normalColor));
dataset.data.push(isNumber(value) ? value : bnToBn(value).toNumber());
data.labels.push(label);
return data;
}, {
datasets: [{
backgroundColor: [] as string[],
data: [] as number[],
hoverBackgroundColor: [] as string[]
}],
labels: [] as string[]
});
return {
chartData,
chartOptions: {
// width/height by default this is "1", i.e. a square box
aspectRatio,
// no need for the legend, expect the labels contain everything
legend: {
display: false
},
scales: {
xAxes: [{
ticks: showLabels
? { beginAtZero: true, max }
: { display: false }
}]
},
tooltips: {
callbacks: {
label: (item: TooltipItem): string =>
values[item.index].tooltip || values[item.index].label
}
}
},
jsonValues
};
}
function ChartHorizBar ({ aspectRatio = 8, className = '', max = 100, showLabels = false, values }: HorizBarProps): React.ReactElement<HorizBarProps> | null {
const [{ chartData, chartOptions, jsonValues }, setState] = useState<State>({});
useEffect((): void => {
const newJsonValues = JSON.stringify(values);
if (newJsonValues !== jsonValues) {
setState(calculateOptions(aspectRatio, values, newJsonValues, max, showLabels));
}
}, [aspectRatio, jsonValues, max, showLabels, values]);
if (!chartData) {
return null;
}
// HACK on width/height to get the aspectRatio to work
return (
<div className={className}>
<HorizontalBar
data={chartData}
height={null as unknown as number}
options={chartOptions}
width={null as unknown as number}
/>
</div>
);
}
export default React.memo(ChartHorizBar);