diff options
| author | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
| commit | 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch) | |
| tree | b9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/components/charts/BarChart.tsx | |
| download | umami-main.tar.xz umami-main.zip | |
Created from https://vercel.com/new
Diffstat (limited to 'src/components/charts/BarChart.tsx')
| -rw-r--r-- | src/components/charts/BarChart.tsx | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/components/charts/BarChart.tsx b/src/components/charts/BarChart.tsx new file mode 100644 index 0000000..7bfc72d --- /dev/null +++ b/src/components/charts/BarChart.tsx @@ -0,0 +1,131 @@ +import { useTheme } from '@umami/react-zen'; +import { useMemo, useState } from 'react'; +import { Chart, type ChartProps } from '@/components/charts/Chart'; +import { ChartTooltip } from '@/components/charts/ChartTooltip'; +import { useLocale } from '@/components/hooks'; +import { renderNumberLabels } from '@/lib/charts'; +import { getThemeColors } from '@/lib/colors'; +import { DATE_FORMATS, formatDate } from '@/lib/date'; +import { formatLongCurrency, formatLongNumber } from '@/lib/format'; + +const dateFormats = { + millisecond: 'T', + second: 'pp', + minute: 'p', + hour: 'p - PP', + day: 'PPPP', + week: 'PPPP', + month: 'LLLL yyyy', + quarter: 'qqq', + year: 'yyyy', +}; + +export interface BarChartProps extends ChartProps { + unit?: string; + stacked?: boolean; + currency?: string; + renderXLabel?: (label: string, index: number, values: any[]) => string; + renderYLabel?: (label: string, index: number, values: any[]) => string; + XAxisType?: string; + YAxisType?: string; + minDate?: Date; + maxDate?: Date; +} + +export function BarChart({ + chartData, + renderXLabel, + renderYLabel, + unit, + XAxisType = 'timeseries', + YAxisType = 'linear', + stacked = false, + minDate, + maxDate, + currency, + ...props +}: BarChartProps) { + const [tooltip, setTooltip] = useState(null); + const { theme } = useTheme(); + const { locale } = useLocale(); + const { colors } = useMemo(() => getThemeColors(theme), [theme]); + + const chartOptions: any = useMemo(() => { + return { + __id: Date.now(), + scales: { + x: { + type: XAxisType, + stacked: true, + min: formatDate(minDate, DATE_FORMATS[unit], locale), + max: formatDate(maxDate, DATE_FORMATS[unit], locale), + offset: true, + time: { + unit, + }, + grid: { + display: false, + }, + border: { + color: colors.chart.line, + }, + ticks: { + color: colors.chart.text, + autoSkip: false, + maxRotation: 0, + callback: renderXLabel, + }, + }, + y: { + type: YAxisType, + min: 0, + beginAtZero: true, + stacked: !!stacked, + grid: { + color: colors.chart.line, + }, + border: { + color: colors.chart.line, + }, + ticks: { + color: colors.chart.text, + callback: renderYLabel || renderNumberLabels, + }, + }, + }, + }; + }, [chartData, colors, unit, stacked, renderXLabel, renderYLabel]); + + const handleTooltip = ({ tooltip }: { tooltip: any }) => { + const { opacity, labelColors, dataPoints } = tooltip; + + setTooltip( + opacity + ? { + title: formatDate( + new Date(dataPoints[0].raw?.d || dataPoints[0].raw?.x || dataPoints[0].raw), + dateFormats[unit], + locale, + ), + color: labelColors?.[0]?.backgroundColor, + value: currency + ? formatLongCurrency(dataPoints[0].raw.y, currency) + : `${formatLongNumber(dataPoints[0].raw.y)} ${dataPoints[0].dataset.label}`, + } + : null, + ); + }; + + return ( + <> + <Chart + {...props} + type="bar" + chartData={chartData} + chartOptions={chartOptions} + onTooltip={handleTooltip} + /> + {tooltip && <ChartTooltip {...tooltip} />} + </> + ); +} |