Графики и статистикаchartsvgzero-dependencyheatmapmatrixsequentialgrid
Heatmap Chart
Матричная тепловая карта с одноцветной последовательной шкалой, значениями и легендой.
Живое превью
Per weekday, % of requests
MonTueWedThuFriSatSun
auth0.20.30.20.40.60.10.1
billing0.81.20.91.62.40.40.3
search0.40.50.70.60.90.30.2
uploads1.11.41.92.83.20.90.6
webhooks0.30.20.40.50.40.20.1
0.1%3.2%
View data
| Mon | Tue | Wed | Thu | Fri | Sat | Sun | |
|---|---|---|---|---|---|---|---|
| auth | 0.2% | 0.3% | 0.2% | 0.4% | 0.6% | 0.1% | 0.1% |
| billing | 0.8% | 1.2% | 0.9% | 1.6% | 2.4% | 0.4% | 0.3% |
| search | 0.4% | 0.5% | 0.7% | 0.6% | 0.9% | 0.3% | 0.2% |
| uploads | 1.1% | 1.4% | 1.9% | 2.8% | 3.2% | 0.9% | 0.6% |
| webhooks | 0.3% | 0.2% | 0.4% | 0.5% | 0.4% | 0.2% | 0.1% |
Использование
Скопируйте и вставьте этот код в ваш React-компонент:
tsx
import { HeatmapChart } from 'react-component-kit-ui/charts';
<HeatmapChart rows={['auth', 'billing']} columns={['Mon', 'Tue', 'Wed']} values={[[0.2, 0.3, 0.2], [0.8, 1.2, 0.9]]} suffix="%" />Ручная установка
Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/HeatmapChart.tsx + HeatmapChart.css:
HeatmapChart.tsx
'use client';
import type { CSSProperties } from 'react';
import ChartFrame from './ChartFrame';
import './HeatmapChart.css';
/** "a: b; --c: d" → style object */
function st(css: string | undefined): CSSProperties {
const o: Record<string, string> = {};
for (const decl of String(css ?? '').split(';')) {
const i = decl.indexOf(':');
if (i < 0) continue;
const k = decl.slice(0, i).trim();
if (k) o[k.startsWith('--') ? k : k.replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = decl.slice(i + 1).trim();
}
return o as CSSProperties;
}
/**
* HeatmapChart — Matrix of cells colored on a single-hue sequential ramp
* (light = low, saturated = high), with a scale legend.
*
* @param {string[]} rows - Row labels.
* @param {string[]} columns - Column labels.
* @param {number[][]} values - values[row][col].
* @param {boolean} showValues - Print values inside cells.
* @param {string} suffix - Value suffix.
* @param {string} color - Ramp hue.
* @param {string} variant - Card style (glass|solid|ghost).
* @param {string} size - Density (sm|md|lg).
*/
export interface HeatmapChartProps {
title?: string;
subtitle?: string;
rows?: string[];
columns?: string[];
values?: number[][];
showValues?: boolean;
suffix?: string;
color?: string;
variant?: 'glass' | 'solid' | 'ghost';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
export function HeatmapChart({
title = 'Error rate by service',
subtitle = 'Per weekday, % of requests',
rows = ['auth', 'billing', 'search', 'uploads', 'webhooks'],
columns = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
values = [
[0.2, 0.3, 0.2, 0.4, 0.6, 0.1, 0.1],
[0.8, 1.2, 0.9, 1.6, 2.4, 0.4, 0.3],
[0.4, 0.5, 0.7, 0.6, 0.9, 0.3, 0.2],
[1.1, 1.4, 1.9, 2.8, 3.2, 0.9, 0.6],
[0.3, 0.2, 0.4, 0.5, 0.4, 0.2, 0.1],
],
showValues = true,
suffix = '%',
color = 'var(--chart-1, #3987e5)',
variant = 'glass',
size = 'md',
className = '',
}: HeatmapChartProps) {
const flat = values.flat();
const min = Math.min(...flat);
const max = Math.max(...flat);
const t = (v: number) => (max === min ? 1 : (v - min) / (max - min));
const fill = (v: number) => `color-mix(in srgb, ${color} ${Math.round(12 + t(v) * 88)}%, #141416)`;
return (
<>
<ChartFrame title={title} subtitle={subtitle} variant={variant} size={size} className={className} table={{ headers: ['', ...columns], rows: rows.map((r, ri) => [r, ...values[ri].map((v) => `${v}${suffix}`)]) }}>
<div className="hm" style={st(`--hm-cols:${columns.length}`)} data-rk-9ca6f8="">
<div className="hm__grid" role="grid" aria-label={title} data-rk-9ca6f8="">
<div className="hm__row" role="row" data-rk-9ca6f8="">
<span className="hm__corner" role="columnheader" data-rk-9ca6f8=""></span>
{columns.map((c, __i: number) => <span key={__i} className="hm__col" role="columnheader" data-rk-9ca6f8="">{c}</span>)}
</div>
{rows.map((r, ri) => (
<div key={ri} className="hm__row" role="row" data-rk-9ca6f8="">
<span className="hm__rowlabel" role="rowheader" data-rk-9ca6f8="">{r}</span>
{columns.map((c, ci) => {
const v = values[ri]?.[ci] ?? 0;
return (
<span className="hm__cell cf-mark" role="gridcell" tabIndex={0} style={st(`background:${fill(v)}`)} aria-label={`${r}, ${c}: ${v}${suffix}`} data-tip-title={`${r} · ${c}`} data-tip={`${v}${suffix}`} data-rk-9ca6f8="">
{showValues && <span className={t(v) > 0.55 ? 'hm__v hm__v--strong' : 'hm__v'} data-rk-9ca6f8="">{v}</span>}
</span>
);
})}
</div>
))}
</div>
<div className="hm__scale" aria-hidden="true" data-rk-9ca6f8="">
<span data-rk-9ca6f8="">{min}{suffix}</span>
<span className="hm__ramp" style={st(`background:linear-gradient(90deg, ${fill(min)}, ${fill(max)})`)} data-rk-9ca6f8=""></span>
<span data-rk-9ca6f8="">{max}{suffix}</span>
</div>
</div>
</ChartFrame>
</>
);
}
export default HeatmapChart;HeatmapChart.css
/* HeatmapChart — ported from astro-component-kit (charts/HeatmapChart.astro) */
.hm:where([data-rk-9ca6f8]) { overflow-x: auto; }
.hm__grid:where([data-rk-9ca6f8]) { display: grid; gap: 3px; min-width: calc(5rem + var(--hm-cols) * 2.4rem); }
.hm__row:where([data-rk-9ca6f8]) {
display: grid;
grid-template-columns: 5rem repeat(var(--hm-cols), minmax(2.2rem, 1fr));
gap: 3px;
}
.hm__col:where([data-rk-9ca6f8]) { text-align: center; font-size: 0.6875rem; color: var(--c-text-3, #8a8a8a); padding-bottom: 0.2rem; }
.hm__rowlabel:where([data-rk-9ca6f8]) {
display: flex;
align-items: center;
font-size: 0.75rem;
color: var(--c-text-2, #a3a3a3);
font-family: var(--font-mono, monospace);
}
.hm__cell:where([data-rk-9ca6f8]) {
display: grid;
place-items: center;
min-height: 34px;
border-radius: 4px;
transition: transform 0.15s ease;
}
.hm__cell:hover:where([data-rk-9ca6f8]) { transform: scale(1.06); }
.hm__cell:focus-visible:where([data-rk-9ca6f8]) { outline: 2px solid var(--c-text-1, #fff); outline-offset: 1px; }
.hm__v:where([data-rk-9ca6f8]) { font-size: 0.6875rem; color: rgb(var(--ink-rgb, 255 255 255) / 0.7); font-variant-numeric: tabular-nums; }
.hm__v--strong:where([data-rk-9ca6f8]) { color: #fff; font-weight: 650; }
.hm__scale:where([data-rk-9ca6f8]) {
display: flex;
align-items: center;
gap: 0.6rem;
margin-top: 0.9rem;
font-size: 0.6875rem;
color: var(--c-text-3, #8a8a8a);
}
.hm__ramp:where([data-rk-9ca6f8]) { flex: 0 1 160px; height: 8px; border-radius: 4px; }Краткая информация
- Категория
- Графики и статистика
- Имя файла
HeatmapChart.tsx- Зависимости
- Нет — чистый React + CSS
- Теги
- chartsvgzero-dependencyheatmapmatrixsequentialgrid