mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
96 lines
2.4 KiB
TypeScript
96 lines
2.4 KiB
TypeScript
import {Fragment} from 'react';
|
|
import styled from '@emotion/styled';
|
|
|
|
import {Tooltip} from 'sentry/components/tooltip';
|
|
import {space} from 'sentry/styles/space';
|
|
import {formatPercentage} from 'sentry/utils/formatters';
|
|
|
|
type Point = {
|
|
label: string;
|
|
value: number;
|
|
active?: boolean;
|
|
onClick?: () => void;
|
|
tooltip?: string;
|
|
};
|
|
|
|
type Props = {
|
|
/**
|
|
* The data to display. The caller should order the points
|
|
* in the order they want bars displayed.
|
|
*/
|
|
data: Point[];
|
|
maxItems?: number;
|
|
};
|
|
|
|
function BreakdownBars({data, maxItems}: Props) {
|
|
const total = data.reduce((sum, point) => point.value + sum, 0);
|
|
return (
|
|
<BreakdownGrid>
|
|
{(maxItems ? data.slice(0, maxItems) : data).map((point, i) => {
|
|
const bar = (
|
|
<Fragment>
|
|
<Bar
|
|
style={{width: `${((point.value / total) * 100).toFixed(2)}%`}}
|
|
active={point.active}
|
|
/>
|
|
<Label>{point.label}</Label>
|
|
</Fragment>
|
|
);
|
|
return (
|
|
<Fragment key={`${i}:${point.label}`}>
|
|
<Percentage>{formatPercentage(point.value / total, 0)}</Percentage>
|
|
<BarContainer
|
|
data-test-id={`status-${point.label}`}
|
|
cursor={point.onClick ? 'pointer' : 'default'}
|
|
onClick={point.onClick}
|
|
>
|
|
{point.tooltip ? <Tooltip title={point.tooltip}>{bar}</Tooltip> : bar}
|
|
</BarContainer>
|
|
</Fragment>
|
|
);
|
|
})}
|
|
</BreakdownGrid>
|
|
);
|
|
}
|
|
|
|
export default BreakdownBars;
|
|
|
|
const BreakdownGrid = styled('div')`
|
|
display: grid;
|
|
grid-template-columns: min-content auto;
|
|
column-gap: ${space(1)};
|
|
row-gap: ${space(1)};
|
|
`;
|
|
|
|
const Percentage = styled('div')`
|
|
font-size: ${p => p.theme.fontSizeExtraLarge};
|
|
text-align: right;
|
|
`;
|
|
|
|
const BarContainer = styled('div')<{cursor: 'pointer' | 'default'}>`
|
|
padding-left: ${space(1)};
|
|
padding-right: ${space(1)};
|
|
position: relative;
|
|
cursor: ${p => p.cursor};
|
|
display: flex;
|
|
align-items: center;
|
|
`;
|
|
|
|
const Label = styled('span')`
|
|
position: relative;
|
|
color: ${p => p.theme.textColor};
|
|
z-index: 2;
|
|
font-size: ${p => p.theme.fontSizeSmall};
|
|
`;
|
|
|
|
const Bar = styled('div')<{active?: boolean}>`
|
|
border-radius: 2px;
|
|
background-color: ${p => (p.active ? p.theme.purple200 : p.theme.border)};
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
z-index: 1;
|
|
height: 100%;
|
|
width: 0%;
|
|
`;
|