UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

119 lines (118 loc) 6.15 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; /* * Copyright 2025 The Kubernetes Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import Box from '@mui/material/Box'; import Paper from '@mui/material/Paper'; import { useTheme } from '@mui/material/styles'; import { styled } from '@mui/material/styles'; import Typography from '@mui/material/Typography'; import { useRef } from 'react'; import ReactDOM from 'react-dom'; import { Bar, BarChart, Label, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts'; import Loader from './Loader'; export function PercentageCircle(props) { const theme = useTheme(); const { data, size = 200, dataKey = 'percentage', label = '', title = '', legend = null, total = 100, totalProps = {}, thickness = 16, } = props; const chartSize = size * 0.8; const isLoading = total < 0; function formatData() { let filledValue = 0; const formattedData = data.map(item => { filledValue += item.value; return { percentage: (item.value / total) * 100, ...item, }; }); const totalValue = total === 0 ? { name: 'total', percentage: 100, value: total, fill: theme.palette.chartStyles.defaultFillColor, } : { name: 'total', percentage: ((total - filledValue) / total) * 100, value: total, fill: theme.palette.chartStyles.defaultFillColor, ...totalProps, }; return formattedData.concat(totalValue); } return (_jsxs(Box, { "aria-busy": isLoading, "aria-live": "polite", justifyContent: "center", alignItems: "center", alignContent: "center", mx: "auto", children: [title && (_jsx(Typography, { sx: { textAlign: 'center', fontSize: '1.2em', flexGrow: 1, fontWeight: 'bold', }, children: title })), isLoading ? (_jsx(Loader, { title: `Loading data for ${title}` })) : (_jsx(PieChart, { cx: size / 2, cy: size / 2, width: chartSize, height: chartSize, style: { marginLeft: 'auto', marginRight: 'auto', }, children: _jsx(Pie, { "aria-label": label || '0%', isAnimationActive: false, data: formatData(), // Center the chart cx: chartSize / 2, cy: chartSize / 2, innerRadius: chartSize * 0.4 - thickness, outerRadius: chartSize * 0.4, dataKey: dataKey, // Start at the top startAngle: 90, endAngle: -270, stroke: theme.palette.chartStyles.defaultFillColor, fill: theme.palette.chartStyles.fillColor || theme.palette.common.black, children: _jsx(Label, { value: label || '', position: "center", style: { fontSize: `${chartSize * 0.15}px`, fill: theme.palette.chartStyles.labelColor, } }) }) })), !isLoading && legend !== null && (_jsx(Typography, { sx: { textAlign: 'center', fontSize: '1.1em', flexGrow: 1, }, children: legend }))] })); } const StyledResponsiveContainer = styled(ResponsiveContainer)({ marginLeft: 'auto', marginRight: 'auto', }); const StyledBarChart = styled(BarChart)(({ theme }) => ({ zIndex: theme.zIndex.drawer, })); export function PercentageBar(props) { const theme = useTheme(); const { data, total = 100, tooltipFunc = null } = props; const containerRef = useRef(null); function formatData() { const dataItems = {}; data.forEach(item => { dataItems[item.name] = (item.value / total) * 100; }); return dataItems; } const barBackground = theme.palette.mode === 'dark' ? theme.palette.grey[800] : theme.palette.grey[300]; const barColor = theme.palette.mode === 'dark' ? theme.palette.primary.light : theme.palette.primary.main; return (_jsx(StyledResponsiveContainer, { width: "95%", height: 20, ref: containerRef, children: _jsxs(StyledBarChart, { layout: "vertical", maxBarSize: 5, data: [formatData()], children: [tooltipFunc && (_jsx(Tooltip, { content: props => (_jsx(PaperTooltip, { rechartsProps: props, tooltipFunc: tooltipFunc, data: data, containerRef: containerRef })) })), _jsx(XAxis, { hide: true, domain: [0, 100], type: "number" }), _jsx(YAxis, { hide: true, type: "category" }), data.map((item, index) => { return (_jsx(Bar, { dataKey: item.name, stackId: "1", fill: item.fill || barColor, layout: "vertical", radius: theme.shape.borderRadius, background: { fill: barBackground } }, index)); })] }) })); } function PaperTooltip({ rechartsProps, tooltipFunc, data, containerRef }) { if (!rechartsProps || !rechartsProps.active || !rechartsProps.coordinate) return null; // eslint-disable-next-line react-hooks/refs const rect = containerRef.current?.getBoundingClientRect(); const { x, y } = rechartsProps.coordinate; const left = (rect?.left || 0) + x; const top = (rect?.top ?? 0) + y - 5; return ReactDOM.createPortal(_jsx(Paper, { className: "custom-tooltip", style: { position: 'fixed', left, top, zIndex: 1500, pointerEvents: 'none', minWidth: 120, transform: 'translate(-50%, -100%)', }, children: _jsx(Box, { m: 1, children: tooltipFunc(data) }) }), document.body); }