import { useState } from 'react' import { Bar, BarChart, Cell, Legend, Tooltip, XAxis } from 'recharts' import dayjs from 'dayjs' import utc from 'dayjs/plugin/utc' import ChartHeader from './ChartHeader' import { CHART_COLORS, DateTimeFormats, DEFAULT_STACK_COLORS, genStackColorScales, ValidStackColor, } from './Charts.constants' import type { CommonChartProps } from './Charts.types' import { numberFormatter, precisionFormatter, timestampFormatter, useChartSize, useStacked, } from './Charts.utils' import NoDataPlaceholder from './NoDataPlaceholder' dayjs.extend(utc) interface Props extends CommonChartProps { xAxisKey: string yAxisKey: string stackKey: string onBarClick?: () => void variant?: 'values' | 'percentages' xAxisFormatAsDate?: boolean displayDateInUtc?: boolean hideLegend?: boolean hideHeader?: boolean stackColors?: ValidStackColor[] } const StackedBarChart: React.FC = ({ size, data, xAxisKey, stackKey, yAxisKey, customDateFormat = DateTimeFormats.FULL, title, highlightedValue, highlightedLabel, format, minimalHeader = false, valuePrecision, onBarClick, variant, xAxisFormatAsDate = true, displayDateInUtc, hideLegend = false, hideHeader = false, stackColors = DEFAULT_STACK_COLORS, }) => { const { Container } = useChartSize(size) const { dataKeys, stackedData, percentagesStackedData } = useStacked({ data, xAxisKey, stackKey, yAxisKey, variant, }) const [focusDataIndex, setFocusDataIndex] = useState(null) const day = (value: number | string) => (displayDateInUtc ? dayjs(value).utc() : dayjs(value)) const resolvedHighlightedLabel = (focusDataIndex !== null && data && data[focusDataIndex] !== undefined && day(data[focusDataIndex][xAxisKey]).format(customDateFormat)) || highlightedLabel const resolvedHighlightedValue = focusDataIndex !== null ? data[focusDataIndex]?.[yAxisKey] : highlightedValue if (!data || data.length === 0) return const stackColorScales = genStackColorScales(stackColors) return (
{!hideHeader && ( )} { if (e.activeTooltipIndex !== focusDataIndex) { setFocusDataIndex(e.activeTooltipIndex) } }} onMouseLeave={() => setFocusDataIndex(null)} > {!hideLegend && ( )} {dataKeys.map((datum, stackIndex) => ( {stackedData?.map((_entry: unknown, index: any) => ( ))} ))} timestampFormatter(label, customDateFormat, displayDateInUtc) : undefined } formatter={(value, name, props) => { const suffix = format || '' if (variant === 'percentages' && percentagesStackedData) { const index = percentagesStackedData.findIndex( (pStack) => pStack === props.payload! ) const val = stackedData[index][name] const percentage = precisionFormatter(Number(value) * 100, 1) + '%' return `${percentage} (${val}${suffix})` } return String(value) + suffix }} cursor={false} labelClassName="text-white" contentStyle={{ backgroundColor: '#444444', borderColor: '#444444', fontSize: '12px', }} wrapperClassName="bg-gray-600 rounded min-w-md" /> {stackedData && stackedData[0] && (
{timestampFormatter( stackedData[0][xAxisKey] as string, customDateFormat, displayDateInUtc )} {timestampFormatter( stackedData[stackedData?.length - 1][xAxisKey] as string, customDateFormat, displayDateInUtc )}
)}
) } export default StackedBarChart