refactor: dashboard usage
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
import { Chart } from '@gpustack/core-ui';
|
||||
import { formatLargeNumber } from '@gpustack/core-ui/utils';
|
||||
import { Empty, Spin, theme } from 'antd';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { generateCoolColors } from '../bar-chart';
|
||||
|
||||
export interface PieChartItem {
|
||||
name: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface PieChartProps {
|
||||
data: PieChartItem[];
|
||||
height?: number | string;
|
||||
width?: number | string;
|
||||
loading?: boolean;
|
||||
colorOffset?: number;
|
||||
total?: number;
|
||||
totalLabel?: string;
|
||||
}
|
||||
|
||||
const PieChart: React.FC<PieChartProps> = ({
|
||||
data,
|
||||
height = 300,
|
||||
width = '100%',
|
||||
loading = false,
|
||||
colorOffset = 0,
|
||||
total,
|
||||
totalLabel
|
||||
}) => {
|
||||
const { token } = theme.useToken();
|
||||
const chartRef = useRef<{ chart: any } | null>(null);
|
||||
|
||||
const colors = useMemo(() => {
|
||||
const generatedColors = generateCoolColors(data.length + colorOffset);
|
||||
return generatedColors.slice(colorOffset);
|
||||
}, [colorOffset, data.length]);
|
||||
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
color: colors,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
backgroundColor: token.colorBgElevated,
|
||||
borderColor: 'transparent',
|
||||
formatter: (params: any) => {
|
||||
return `<div class="tooltip-wrapper">
|
||||
<span class="tooltip-item">
|
||||
<span class="tooltip-item-name">
|
||||
<span class="tooltip-item-dot" style="border-radius:50%;background-color:${params.color};"></span>
|
||||
<span class="tooltip-item-title">${params.name}</span>:
|
||||
</span>
|
||||
<span class="tooltip-value">${formatLargeNumber(params.value)}</span>
|
||||
</span>
|
||||
</div>`;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 0,
|
||||
top: 18,
|
||||
bottom: 18,
|
||||
itemWidth: 8,
|
||||
itemHeight: 8,
|
||||
itemGap: 12,
|
||||
textStyle: {
|
||||
color: token.colorTextTertiary,
|
||||
overflow: 'truncate',
|
||||
width: 180
|
||||
},
|
||||
pageTextStyle: { color: token.colorTextTertiary },
|
||||
pageIconColor: token.colorTextTertiary,
|
||||
pageIconInactiveColor: token.colorTextDisabled,
|
||||
data: data.map((item) => item.name),
|
||||
show: data.length > 0
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['52%', '72%'],
|
||||
center: ['34%', '50%'],
|
||||
avoidLabelOverlap: true,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
formatter: (params: any) => `${params.percent}%`,
|
||||
fontSize: 16,
|
||||
fontWeight: 600,
|
||||
color: token.colorText
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data
|
||||
}
|
||||
]
|
||||
}),
|
||||
[colors, data, token]
|
||||
);
|
||||
|
||||
if (!data.length) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<Spin size="middle" />
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const displayTotal = total ?? data.reduce((sum, item) => sum + item.value, 0);
|
||||
|
||||
return (
|
||||
<div style={{ width, height, position: 'relative' }}>
|
||||
<Chart
|
||||
ref={chartRef as any}
|
||||
options={options as any}
|
||||
height={height}
|
||||
width={width}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: '34%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
pointerEvents: 'none',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 20,
|
||||
fontWeight: 600,
|
||||
color: token.colorText,
|
||||
lineHeight: 1.2
|
||||
}}
|
||||
>
|
||||
{formatLargeNumber(displayTotal)}
|
||||
</span>
|
||||
{totalLabel && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: token.colorTextTertiary,
|
||||
marginTop: 4
|
||||
}}
|
||||
>
|
||||
{totalLabel}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{loading && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'var(--ant-color-bg-container)',
|
||||
opacity: 0.6,
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
>
|
||||
<Spin size="middle" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PieChart;
|
||||
Reference in New Issue
Block a user