import { convertFileSize } from '@/utils'; import { useIntl } from '@umijs/max'; import { Flex, Popover, theme } from 'antd'; import { createStyles } from 'antd-style'; import type { GlobalToken } from 'antd/es/theme'; import React from 'react'; export interface DualArcGaugeItem { index: number; label?: string; memory: { total: number; // bytes used: number; // bytes — outer arc (semantic color) allocated: number; // bytes — inner arc (blue) }; } interface DualArcGaugeProps { data: DualArcGaugeItem[]; } // guage external radius: 20, internal radius: 16, stroke width: 2 const CX = 25; const CY = 24; const R_OUT = 20; const R_IN = 16; const SW = 2.5; const L_OUT = Math.PI * R_OUT; const L_IN = Math.PI * R_IN; const arcPath = (r: number) => `M ${CX - r} ${CY} A ${r} ${r} 0 0 1 ${CX + r} ${CY}`; const P_OUT = arcPath(R_OUT); const P_IN = arcPath(R_IN); const clamp01 = (n: number) => Math.min(Math.max(n, 0), 1); // Used color: green < 75%, yellow < 90%, red >= 90% const usedColor = (ratio: number, token: GlobalToken) => { if (ratio >= 0.9) return token.colorError; if (ratio >= 0.75) return token.colorWarning; return token.colorSuccess; }; const useStyles = createStyles(({ css, token }) => ({ row: css` width: fit-content; padding: 3px 6px; border-radius: ${token.borderRadius}px; cursor: pointer; transition: background-color 0.15s; &:hover { background-color: ${token.colorFillTertiary}; } `, index: css` font-size: 12px; font-weight: 500; color: ${token.colorTextSecondary}; border-bottom: 1px dashed var(--ant-blue-6); padding-bottom: 3px; line-height: 1.2; `, popTitle: css` font-size: 12px; font-weight: 500; color: ${token.colorText}; margin-bottom: 6px; `, popList: css` font-size: 12px; font-variant-numeric: tabular-nums; `, metric: css` white-space: nowrap; `, dot: css` width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; `, value: css` min-width: 64px; text-align: right; font-weight: 500; color: ${token.colorText}; `, label: css` color: ${token.colorTextTertiary}; ` })); const DualArcGauge: React.FC = ({ data }) => { const intl = useIntl(); const { styles } = useStyles(); const { token } = theme.useToken(); const trackColor = token.colorFillSecondary; const allocColor = 'var(--ant-blue-6)'; return ( {data.map((item) => { const { total = 0, used = 0, allocated = 0 } = item.memory || {}; const usedR = total > 0 ? clamp01(used / total) : 0; const allocR = total > 0 ? clamp01(allocated / total) : 0; const pct = Math.round(usedR * 100); const uc = usedColor(usedR, token); // set dim color for allocated if used is 0, otherwise use the same color as used const allocDotColor = allocated > 0 ? allocColor : token.colorTextQuaternary; const content = (
[{item.index}]{item.label ? ` ${item.label}` : ''} ·{' '} {convertFileSize(total)}
{convertFileSize(used)} {intl.formatMessage({ id: 'resources.table.used' })} {convertFileSize(allocated)} {intl.formatMessage({ id: 'resources.table.allocated' })}
); return ( {usedR > 0 && ( )} {allocR > 0 && ( )} {pct}% {[{item.index}]} ); })}
); }; export default DualArcGauge;