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 { useState, useMemo, useRef, useCallback, useEffect } from 'react'
|
||||||
import { useTheme } from '@ciphera-net/ui'
|
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 { 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 { ChartLine, ChartBar } from '@phosphor-icons/react'
|
||||||
import { Card, CardContent, CardHeader } from '@/components/ui/card'
|
import { Card, CardContent, CardHeader } from '@/components/ui/card'
|
||||||
import { formatNumber, formatDuration, formatUpdatedAgo, DatePicker } from '@ciphera-net/ui'
|
import { formatNumber, formatDuration, formatUpdatedAgo, DatePicker } from '@ciphera-net/ui'
|
||||||
@@ -492,6 +492,13 @@ export default function Chart({
|
|||||||
lineCap="round"
|
lineCap="round"
|
||||||
/>
|
/>
|
||||||
<VisxBarXAxis maxLabels={8} />
|
<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
|
<VisxBarChartTooltip
|
||||||
rows={(point) => {
|
rows={(point) => {
|
||||||
const config = METRIC_CONFIGS.find((m) => m.key === metric)
|
const config = METRIC_CONFIGS.find((m) => m.key === metric)
|
||||||
|
|||||||
@@ -583,6 +583,50 @@ export function BarXAxis({ tickerHalfWidth = 50, showAllLabels = false, maxLabel
|
|||||||
|
|
||||||
BarXAxis.displayName = "BarXAxis";
|
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 ─────────────────────────────────────────────────────────────────────
|
// ─── Bar ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export interface BarProps {
|
export interface BarProps {
|
||||||
|
|||||||
Reference in New Issue
Block a user