fix: add numeric Y-axis to bar chart view

This commit is contained in:
Usman Baig
2026-03-21 22:59:41 +01:00
parent 830da49c5f
commit d4e4ca819c
2 changed files with 52 additions and 1 deletions

View File

@@ -3,7 +3,7 @@
import { useState, useMemo, useRef, useCallback, useEffect } from 'react'
import { useTheme } from '@ciphera-net/ui'
import { AreaChart as VisxAreaChart, Area as VisxArea, Grid as VisxGrid, XAxis as VisxXAxis, YAxis as VisxYAxis, ChartTooltip as VisxChartTooltip, type TooltipRow } from '@/components/ui/area-chart'
import { BarChart as VisxBarChart, Bar as VisxBar, Grid as VisxBarGrid, BarXAxis as VisxBarXAxis, ChartTooltip as VisxBarChartTooltip } from '@/components/ui/bar-chart'
import { BarChart as VisxBarChart, Bar as VisxBar, Grid as VisxBarGrid, BarXAxis as VisxBarXAxis, BarValueAxis as VisxBarYAxis, ChartTooltip as VisxBarChartTooltip } from '@/components/ui/bar-chart'
import { ChartLine, ChartBar } from '@phosphor-icons/react'
import { Card, CardContent, CardHeader } from '@/components/ui/card'
import { formatNumber, formatDuration, formatUpdatedAgo, DatePicker } from '@ciphera-net/ui'
@@ -492,6 +492,13 @@ export default function Chart({
lineCap="round"
/>
<VisxBarXAxis maxLabels={8} />
<VisxBarYAxis
numTicks={6}
formatValue={(v) => {
const config = METRIC_CONFIGS.find((m) => m.key === metric)
return config ? config.format(v) : v.toString()
}}
/>
<VisxBarChartTooltip
rows={(point) => {
const config = METRIC_CONFIGS.find((m) => m.key === metric)

View File

@@ -583,6 +583,50 @@ export function BarXAxis({ tickerHalfWidth = 50, showAllLabels = false, maxLabel
BarXAxis.displayName = "BarXAxis";
// ─── BarValueAxis (numeric Y-axis for vertical bar charts) ───────────────
export interface BarValueAxisProps {
numTicks?: number;
formatValue?: (value: number) => string;
}
export function BarValueAxis({ numTicks = 5, formatValue }: BarValueAxisProps) {
const { yScale, margin, containerRef } = useChart();
const [container, setContainer] = useState<HTMLDivElement | null>(null);
useEffect(() => { setContainer(containerRef.current); }, [containerRef]);
const ticks = useMemo(() => {
const domain = yScale.domain() as [number, number];
const min = domain[0];
const max = domain[1];
const step = (max - min) / (numTicks - 1);
return Array.from({ length: numTicks }, (_, i) => {
const value = min + step * i;
return {
value,
y: (yScale(value) ?? 0) + margin.top,
label: formatValue ? formatValue(value) : value >= 1000 ? `${(value / 1000).toFixed(value % 1000 === 0 ? 0 : 1)}k` : Math.round(value).toLocaleString(),
};
});
}, [yScale, margin.top, numTicks, formatValue]);
if (!container) return null;
return createPortal(
<div className="pointer-events-none absolute inset-0">
{ticks.map((tick) => (
<div key={tick.value} className="absolute" style={{ left: 0, top: tick.y, width: margin.left - 8, display: "flex", justifyContent: "flex-end", transform: "translateY(-50%)" }}>
<span className="whitespace-nowrap text-neutral-500 text-xs tabular-nums">{tick.label}</span>
</div>
))}
</div>,
container
);
}
BarValueAxis.displayName = "BarValueAxis";
// ─── Bar ─────────────────────────────────────────────────────────────────────
export interface BarProps {