fix(ui): сохранять пропуски в мини-графиках измерений

This commit is contained in:
Denozordec committed 2026-09-24 15:58:23 +07:00
1 parent f3568d836b
commit 1d98a2a66e
3 files changed
+52 -15

No files matched your search

+25 -14
View File
@@ -1,5 +1,7 @@
import { Fragment } from "react"
interface SparklineProps {
data: number[]
data: Array<number | null | undefined>
width?: number
height?: number
color?: string
@@ -7,25 +9,34 @@ interface SparklineProps {
}
export function Sparkline({ data, width = 120, height = 28, color = "currentColor", filled = false }: SparklineProps) {
const valid = data.filter((v) => v != null && isFinite(v))
if (valid.length < 2) return <span className="text-muted-foreground/50 text-xs font-mono">—</span>
const valid = data.filter((value): value is number => typeof value === "number" && Number.isFinite(value))
if (!valid.length) return <span className="text-muted-foreground/50 text-xs font-mono" aria-label="Нет истории измерений">—</span>
const max = Math.max(...valid)
const min = Math.min(...valid)
const span = Math.max(1, max - min)
const pts = data.map((v, i) => [
(i / (data.length - 1)) * width,
height - ((v - min) / span) * (height - 2) - 1,
])
const lineD = pts.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`).join(" ")
const areaD = `${lineD} L${width},${height} L0,${height} Z`
const segments: Array<Array<[number, number]>> = []
let segment: Array<[number, number]> = []
data.forEach((value, index) => {
if (typeof value !== "number" || !Number.isFinite(value)) { segment = []; return }
if (!segment.length) segments.push(segment)
segment.push([
data.length === 1 ? width / 2 : index / (data.length - 1) * width,
height - ((value - min) / span) * (height - 2) - 1,
])
})
return (
<svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{ display: "block" }}>
{filled && <path d={areaD} style={{ fill: color, fillOpacity: 0.15 }} />}
<path d={lineD} fill="none" style={{ stroke: color }} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<svg role="img" aria-label="История измерений" width={width} height={height} viewBox={`0 0 ${width} ${height}`} style={{ display: "block" }}>
{segments.map((points, index) => {
if (points.length === 1) return <circle key={index} cx={points[0][0]} cy={points[0][1]} r="1.5" fill={color} />
const line = points.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`).join(" ")
const area = `${line} L${points[points.length - 1][0]},${height} L${points[0][0]},${height} Z`
return <Fragment key={index}>
{filled && <path d={area} style={{ fill: color, fillOpacity: 0.15 }} />}
<path d={line} fill="none" style={{ stroke: color }} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</Fragment>
})}
</svg>
)
}