110 lines
2.9 KiB
TypeScript
110 lines
2.9 KiB
TypeScript
// src/widgets/audioLive/ui/FrequencyHistoryChart.tsx
|
||
import { memo, useMemo } from "react";
|
||
import {
|
||
Line,
|
||
LineChart,
|
||
ResponsiveContainer,
|
||
Tooltip,
|
||
XAxis,
|
||
YAxis,
|
||
} from "recharts";
|
||
import {
|
||
Card,
|
||
CardContent,
|
||
CardHeader,
|
||
CardTitle,
|
||
} from "../../../shared/ui/card";
|
||
import type { AudioSample } from "../../../entities/audioSample/model/types";
|
||
import { formatTimeHHMMSS } from "../../../shared/lib/time";
|
||
|
||
type Props = {
|
||
history?: AudioSample[];
|
||
};
|
||
|
||
type ChartPoint = {
|
||
timeMs: number;
|
||
freq_hz: number;
|
||
};
|
||
|
||
const Y_MIN = 129;
|
||
const Y_MAX = 5500;
|
||
|
||
const Y_TICKS = [139, 200, 500, 1000, 2000, 5000, 5500];
|
||
|
||
function formatHzTick(v: number): string {
|
||
const n = Number(v);
|
||
if (!Number.isFinite(n)) return "";
|
||
if (n >= 1000) return `${(n / 1000).toFixed(n % 1000 === 0 ? 0 : 1)}k`;
|
||
return `${Math.round(n)}`;
|
||
}
|
||
|
||
export const FrequencyHistoryChart = memo(function FrequencyHistoryChart({
|
||
history = [],
|
||
}: Props) {
|
||
const data: ChartPoint[] = useMemo(() => {
|
||
if (!history?.length) return [];
|
||
return history.map((s) => ({ timeMs: s.timeMs, freq_hz: s.freq_hz }));
|
||
}, [history]);
|
||
|
||
const hasData = data.length > 0;
|
||
|
||
return (
|
||
<Card>
|
||
<CardHeader className="pb-2">
|
||
<CardTitle className="text-base">Доминантная частота</CardTitle>
|
||
</CardHeader>
|
||
|
||
<CardContent>
|
||
<div className="relative h-56 min-h-[14rem] w-full">
|
||
{!hasData && (
|
||
<div className="absolute inset-0 grid place-items-center text-sm text-muted-foreground">
|
||
Пока нет данных
|
||
</div>
|
||
)}
|
||
|
||
<ResponsiveContainer width="100%" height="100%">
|
||
<LineChart
|
||
data={data}
|
||
margin={{ top: 10, right: 12, left: 0, bottom: 0 }}
|
||
>
|
||
<XAxis
|
||
dataKey="timeMs"
|
||
type="number"
|
||
domain={["dataMin", "dataMax"]}
|
||
tickFormatter={(v) => formatTimeHHMMSS(Number(v))}
|
||
tick={{ fontSize: 12 }}
|
||
interval="preserveStartEnd"
|
||
/>
|
||
|
||
<YAxis
|
||
type="number"
|
||
scale="log"
|
||
domain={[Y_MIN, Y_MAX]}
|
||
ticks={Y_TICKS}
|
||
allowDataOverflow
|
||
tick={{ fontSize: 12 }}
|
||
width={52}
|
||
tickFormatter={(v) => formatHzTick(Number(v))}
|
||
/>
|
||
|
||
<Tooltip
|
||
labelFormatter={(v) => formatTimeHHMMSS(Number(v))}
|
||
formatter={(v) => [`${Number(v).toFixed(0)} Гц`, "част."]}
|
||
/>
|
||
|
||
<Line
|
||
type="monotone"
|
||
dataKey="freq_hz"
|
||
stroke="oklch(0.208 0.042 265.755)"
|
||
strokeWidth={2}
|
||
dot={false}
|
||
isAnimationActive={false}
|
||
/>
|
||
</LineChart>
|
||
</ResponsiveContainer>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
});
|