Files
2026-07-13 13:32:57 +08:00

44 lines
1.6 KiB
TypeScript

import { useMemo } from "react";
import { formatNumberCompact } from "~/utils/numberFormatter";
/**
* Default y-axis tick formatter: compact notation (8000 → "8K", 1_200_000 → "1.2M").
* Chart.Bar / Chart.Line use it unless the caller supplies its own tickFormatter.
*/
export const defaultYAxisTickFormatter = (value: any): string =>
typeof value === "number" ? formatNumberCompact(value) : String(value);
// 1ch at 11px tabular-nums system-ui ≈ 6.5px. Recharts' YAxis.width prop is a
// raw number (pixels), so we can't use the CSS `ch` unit directly — but tabular-nums
// guarantees uniform char width, which makes `chars * pxPerCh` a faithful proxy.
const PX_PER_CH = 6.5;
const PADDING_PX = 16;
const MIN_WIDTH = 32;
const MAX_WIDTH = 120;
export function useYAxisWidth(
data: Array<Record<string, any>> | undefined,
visibleSeries: string[],
tickFormatter?: (value: any, index: number) => string
): number {
return useMemo(() => {
if (!data?.length || !visibleSeries.length) return MIN_WIDTH;
let max = 0;
for (const point of data) {
for (const key of visibleSeries) {
const v = Number(point[key]);
if (Number.isFinite(v) && v > max) max = v;
}
}
const fmt =
tickFormatter ?? ((v: any) => (typeof v === "number" ? v.toLocaleString() : String(v)));
const label = fmt(max, 0);
// Add one char of slack because recharts "nices" the domain up beyond data max.
const charCount = label.length + 1;
const width = Math.ceil(charCount * PX_PER_CH) + PADDING_PX;
return Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, width));
}, [data, visibleSeries, tickFormatter]);
}