1 звезда на GitHub = 1 новый компонент.

Поставить звезду
Графики и статистикаchartsvgzero-dependencyheatmapmatrixsequentialgrid

Heatmap Chart

Матричная тепловая карта с одноцветной последовательной шкалой, значениями и легендой.

Живое превью

Error rate by service

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
View data
MonTueWedThuFriSatSun
auth0.2%0.3%0.2%0.4%0.6%0.1%0.1%
billing0.8%1.2%0.9%1.6%2.4%0.4%0.3%
search0.4%0.5%0.7%0.6%0.9%0.3%0.2%
uploads1.1%1.4%1.9%2.8%3.2%0.9%0.6%
webhooks0.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