import {Fragment} from 'react'; import isPropValid from '@emotion/is-prop-valid'; import styled from '@emotion/styled'; import {LocationDescriptor} from 'history'; import {TagSegment} from 'sentry/actionCreators/events'; import Link from 'sentry/components/links/link'; import Tooltip from 'sentry/components/tooltip'; import Version from 'sentry/components/version'; import {t} from 'sentry/locale'; import space from 'sentry/styles/space'; import {percent} from 'sentry/utils'; type Props = { segments: TagSegment[]; title: string; totalValues: number; hasError?: boolean; isLoading?: boolean; onTagClick?: (title: string, value: TagSegment) => void; renderEmpty?: () => React.ReactNode; renderError?: () => React.ReactNode; renderLoading?: () => React.ReactNode; showReleasePackage?: boolean; }; type SegmentValue = { index: number; onClick: () => void; to: LocationDescriptor; }; function TagDistributionMeter({ isLoading = false, hasError = false, renderLoading = () => null, renderEmpty = () =>

{t('No recent data.')}

, renderError = () => null, showReleasePackage = false, segments, title, totalValues, onTagClick, }: Props) { function renderTitle() { if (!Array.isArray(segments) || segments.length <= 0) { return ( <TitleType>{title}</TitleType> ); } const largestSegment = segments[0]; const pct = percent(largestSegment.count, totalValues); const pctLabel = Math.floor(pct); const renderLabel = () => { switch (title) { case 'release': return ( ); default: return ; } }; return ( <TitleType>{title}</TitleType> <TitleDescription> {renderLabel()} {isLoading || hasError ? null : <Percent>{pctLabel}%</Percent>} </TitleDescription> ); } function renderSegments() { if (isLoading) { return renderLoading(); } if (hasError) { return {renderError()}; } if (totalValues === 0) { return {renderEmpty()}; } return ( {segments.map((value, index) => { const pct = percent(value.count, totalValues); const pctLabel = Math.floor(pct); const renderTooltipValue = () => { switch (title) { case 'release': return ( ); default: return value.name || t('n/a'); } }; const tooltipHtml = (
{renderTooltipValue()}
{pctLabel}%
); const segmentProps: SegmentValue = { index, to: value.url, onClick: () => onTagClick?.(title, value), }; return (
{value.isOther ? ( ) : ( )}
); })}
); } const totalVisible = segments.reduce((sum, value) => sum + value.count, 0); const hasOther = totalVisible < totalValues; if (hasOther) { segments.push({ isOther: true, name: t('Other'), value: 'other', count: totalValues - totalVisible, url: '', }); } return ( {renderTitle()} {renderSegments()} ); } export default TagDistributionMeter; const COLORS = [ '#3A3387', '#5F40A3', '#8C4FBD', '#B961D3', '#DE76E4', '#EF91E8', '#F7B2EC', '#FCD8F4', '#FEEBF9', ]; const TagSummary = styled('div')` margin-bottom: ${space(1)}; `; const SegmentBar = styled('div')` display: flex; overflow: hidden; border-radius: ${p => p.theme.borderRadius}; `; const Title = styled('div')` display: flex; font-size: ${p => p.theme.fontSizeSmall}; justify-content: space-between; margin-bottom: ${space(0.25)}; line-height: 1.1; `; const TitleType = styled('div')` color: ${p => p.theme.textColor}; font-weight: bold; ${p => p.theme.overflowEllipsis}; `; const TitleDescription = styled('div')` display: flex; color: ${p => p.theme.gray300}; text-align: right; `; const Label = styled('div')` ${p => p.theme.overflowEllipsis}; max-width: 150px; `; const Percent = styled('div')` font-weight: bold; font-variant-numeric: tabular-nums; padding-left: ${space(0.5)}; color: ${p => p.theme.textColor}; `; const OtherSegment = styled('span')` display: block; width: 100%; height: 16px; color: inherit; outline: none; background-color: ${COLORS[COLORS.length - 1]}; `; const Segment = styled(Link, {shouldForwardProp: isPropValid})` display: block; width: 100%; height: 16px; color: inherit; outline: none; background-color: ${p => COLORS[p.index]}; border-radius: 0; `;