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

Поставить звезду
Графики и статистикаchartsvgzero-dependencypunch-cardactivitytime-of-daydots

Punch Card Chart

Матрица «день × час», где площадь точки отражает объём активности.

Живое превью

Deploy activity

Deploys by weekday and hour (UTC)

1,360total deploys
MonTueWedThuFriSatSun00:0002:0004:0006:0008:0010:0012:0014:0016:0018:0020:0022:00
View data
Day00:0002:0004:0006:0008:0010:0012:0014:0016:0018:0020:0022:00
Mon45282536333938231311
Tue6245294234453927178
Wed84172644364241241910
Thu56342739364236251512
Fri6355193326363017147
Sat1415810111213845
Sun30329107119952

Использование

Скопируйте и вставьте этот код в ваш React-компонент:

tsx
import { PunchCardChart } from 'react-component-kit-ui/charts';

<PunchCardChart days={['Mon', 'Tue']} hours={[9, 12, 15, 18]} values={[[4, 12, 9, 2], [6, 15, 11, 3]]} unit="deploys" />

Ручная установка

Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/PunchCardChart.tsx + PunchCardChart.css:

PunchCardChart.tsx
'use client';

import ChartFrame from './ChartFrame';

const defaultHours = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22];
const shape = [0.1, 0.05, 0.02, 0.1, 0.6, 0.95, 0.8, 1, 0.9, 0.55, 0.35, 0.2];
const dayW = [0.9, 1, 1, 0.95, 0.75, 0.25, 0.2];

/**
 * PunchCardChart — Day × hour dot matrix where dot area encodes volume.
 * Classic "when are people active" view.
 *
 * @param {string[]} days - Row labels.
 * @param {number[]} hours - Column hours (0–23).
 * @param {number[][]} values - values[day][hourIndex].
 * @param {string} unit - Unit noun for tooltips (e.g. 'commits').
 * @param {string} color - Dot color.
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
export interface PunchCardChartProps {
  title?: string;
  subtitle?: string;
  days?: string[];
  hours?: number[];
  values?: number[][];
  unit?: string;
  color?: string;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function PunchCardChart({
  title = 'Deploy activity',
  subtitle = 'Deploys by weekday and hour (UTC)',
  days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
  hours = defaultHours,
  values = dayW.map((d, di) => shape.map((s, hi) => Math.round(d * s * 42 + ((di * 7 + hi * 3) % 5)))),
  unit = 'deploys',
  color = 'var(--chart-3, #199e70)',
  variant = 'glass',
  size = 'md',
  className = '',
}: PunchCardChartProps) {
  const W = 640;
  const m = { t: 10, r: 10, b: 26, l: 46 };
  const cols = hours.length;
  const cellW = (W - m.l - m.r) / cols;
  const cellH = 34;
  const H = m.t + days.length * cellH + m.b;
  const max = Math.max(...values.flat()) || 1;
  const rMax = Math.min(cellW, cellH) / 2 - 2;
  const pad = (h: number) => `${String(h).padStart(2, '0')}:00`;
  const total = values.flat().reduce((a, b) => a + b, 0);
  return (
    <>
      <ChartFrame title={title} subtitle={subtitle} value={total.toLocaleString('en-US')} delta={`total ${unit}`} variant={variant} size={size} className={className} table={{ headers: ['Day', ...hours.map(pad)], rows: days.map((d, di) => [d, ...values[di]]) }}>
        <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: punch card of ${unit} by day and hour`}>
          {days.map((d, di) => (
            <g key={di}>
              <line className="cf-grid" x1={m.l} x2={W - m.r} y1={m.t + di * cellH + cellH / 2} y2={m.t + di * cellH + cellH / 2} />
              <text className="cf-label" x={m.l - 10} y={m.t + di * cellH + cellH / 2 + 4} textAnchor="end">{d}</text>
            </g>
          ))}
          {hours.map((h, hi) => <text key={hi} className="cf-label" x={m.l + hi * cellW + cellW / 2} y={H - 8} textAnchor="middle">{pad(h)}</text>)}
          {days.map((d, di) => hours.map((h, hi) => {
            const v = values[di]?.[hi] ?? 0;
            const rad = v ? 2 + Math.sqrt(v / max) * (rMax - 2) : 0;
            const cx = m.l + hi * cellW + cellW / 2;
            const cy = m.t + di * cellH + cellH / 2;
            return (
              <g className="cf-mark cf-dim" tabIndex={0} role="img" aria-label={`${d} ${pad(h)}: ${v} ${unit}`} data-tip-title={`${d} · ${pad(h)}`} data-tip={`${color}::${v} ${unit}`}>
                <rect x={cx - cellW / 2} y={cy - cellH / 2} width={cellW} height={cellH} fill="transparent" />
                {rad > 0 && <circle cx={cx} cy={cy} r={rad} fill={color} fillOpacity={0.35 + (v / max) * 0.65} />}
              </g>
            );
          }))}
        </svg>
      </ChartFrame>
    </>
  );
}

export default PunchCardChart;

Краткая информация

Категория
Графики и статистика
Имя файла
PunchCardChart.tsx
Зависимости
Нет — чистый React + CSS
Теги
chartsvgzero-dependencypunch-cardactivitytime-of-daydots