From eac6228dde6daf9eb299567031e3aa23967b60bb Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Tue, 20 May 2025 17:04:16 -0700 Subject: [PATCH] Stacked bar component --- src/common/components/StackedBarBreakdown.tsx | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/common/components/StackedBarBreakdown.tsx diff --git a/src/common/components/StackedBarBreakdown.tsx b/src/common/components/StackedBarBreakdown.tsx new file mode 100644 index 000000000..8893e321e --- /dev/null +++ b/src/common/components/StackedBarBreakdown.tsx @@ -0,0 +1,85 @@ +import * as React from 'react'; + +import { Box, Typography } from '@mui/joy'; + + +/** Generic segment interface for stacked bar charts */ +export interface StackedBarSegment { + key: string; + label: string; + value: number; + color: string; +} + +export interface StackedBarBreakdownProps { + /** Array of segments to display */ + segments: StackedBarSegment[]; + /** Optional title for the chart */ + title?: string; + /** Whether to show absolute values in the legend (otherwise just percentages) */ + showValues?: boolean; + /** Function to format values for display and tooltips */ + valueFormatter?: (value: number) => string; + /** Optional description text below the chart */ + description?: string; +} + + +export function StackedBarBreakdown({ segments, title, showValues = false, valueFormatter = (v) => v.toString(), description }: StackedBarBreakdownProps) { + // Calculate total for percentages + const total = segments.reduce((sum, seg) => sum + seg.value, 0) || 1; + + // Filter out zero-value segments + const nonZeroSegments = segments.filter(seg => seg.value > 0); + + return ( + + {title && {title}} + + {/* The stacked bar */} + + {nonZeroSegments.map(({ key, value, color }) => { + const percentage = (value / total) * 100; + return ( + + ); + })} + + + {/* Legend with colored squares */} + + {nonZeroSegments.map(({ key, label, value, color }) => { + const percentage = (value / total) * 100; + return ( + + + + {label} {showValues + ? `${valueFormatter(value)} (${percentage.toFixed(0)}%)` + : `(${percentage.toFixed(0)}%)`} + + + ); + })} + + + {description && ( + {description} + )} + + ); +}