import { useState } from 'react' import { Bar, BarChart, Cell, Legend, Tooltip, XAxis } from 'recharts' 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, useChartSize, useStacked } from './Charts.utils' import NoDataPlaceholder from './NoDataPlaceholder' import { useChartHoverState } from './useChartHoverState' import { formatDateTime, useFormatDateTime } from '@/lib/datetime' 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[] syncId?: string } 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, syncId, }) => { const { Container } = useChartSize(size) const { hoveredIndex, syncTooltip, setHover, clearHover } = useChartHoverState( syncId || 'default' ) const { dataKeys, stackedData, percentagesStackedData } = useStacked({ data, xAxisKey, stackKey, yAxisKey, variant, }) const [focusDataIndex, setFocusDataIndex] = useState(null) // When `displayDateInUtc` is set the chart explicitly wants UTC labels. // Otherwise honour the user's selected timezone via the picker. const formatPickerDate = useFormatDateTime() const formatChartDate = (value: number | string) => displayDateInUtc ? formatDateTime(value, { tz: 'UTC', format: customDateFormat }) : formatPickerDate(value, customDateFormat) const resolvedHighlightedLabel = (focusDataIndex !== null && data && data[focusDataIndex] !== undefined && formatChartDate(data[focusDataIndex][xAxisKey])) || 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) } setHover(e.activeTooltipIndex) }} onMouseLeave={() => { setFocusDataIndex(null) clearHover() }} > {!hideLegend && ( )} {dataKeys.map((datum, stackIndex) => ( {stackedData?.map((_entry: unknown, index: any) => ( ))} ))} formatChartDate(label as number | string) : 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-sm min-w-md" active={!!syncId && syncTooltip && hoveredIndex !== null} /> {stackedData && stackedData[0] && (
{formatChartDate(stackedData[0][xAxisKey] as number | string)} {formatChartDate(stackedData[stackedData?.length - 1][xAxisKey] as number | string)}
)}
) } export default StackedBarChart