fix: add numeric Y-axis to bar chart view
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user