1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
Gráficos y Estadísticas chartsvgzero-dependencypunch-cardactivitytime-of-daydots

Punch Card Chart

Matriz día × hora de puntos cuyo tamaño codifica el volumen de actividad.

Vista Previa en Vivo

Deploy activity

Deploys by weekday and hour (UTC)

1,360 total deploys
Mon Tue Wed Thu Fri Sat Sun 00: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

Uso

Copia y pega este código directamente en tu página de Astro:

astro
---
import { PunchCardChart } from 'astro-component-kit/charts';
---

<PunchCardChart
  days={['Mon', 'Tue']}
  hours={[9, 12, 15, 18]}
  values={[[4, 12, 9, 2], [6, 15, 11, 3]]}
  unit="deploys"
/>
--- import { PunchCardChart } from 'astro-component-kit/charts'; --- <PunchCardChart days={['Mon', 'Tue']} hours={[9, 12, 15, 18]} values={[[4, 12, 9, 2], [6, 15, 11, 3]]} unit="deploys" />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/lib/PunchCardChart.astro:

astro
---
/**
 * 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).
 */
import ChartFrame from './ChartFrame.astro';

interface Props {
  title?: string;
  subtitle?: string;
  days?: string[];
  hours?: number[];
  values?: number[][];
  unit?: string;
  color?: string;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

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];

const {
  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 = '',
} = Astro.props;

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);
---

<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>
        <line class="cf-grid" x1={m.l} x2={W - m.r} y1={m.t + di * cellH + cellH / 2} y2={m.t + di * cellH + cellH / 2} />
        <text class="cf-label" x={m.l - 10} y={m.t + di * cellH + cellH / 2 + 4} text-anchor="end">{d}</text>
      </g>
    ))}
    {hours.map((h, hi) => <text class="cf-label" x={m.l + hi * cellW + cellW / 2} y={H - 8} text-anchor="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 class="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} fill-opacity={0.35 + (v / max) * 0.65} />}
        </g>
      );
    }))}
  </svg>
</ChartFrame>
--- /** * 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). */ import ChartFrame from './ChartFrame.astro'; interface Props { title?: string; subtitle?: string; days?: string[]; hours?: number[]; values?: number[][]; unit?: string; color?: string; variant?: 'glass' | 'solid' | 'ghost'; size?: 'sm' | 'md' | 'lg'; className?: string; } 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]; const { 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 = '', } = Astro.props; 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); --- <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> <line class="cf-grid" x1={m.l} x2={W - m.r} y1={m.t + di * cellH + cellH / 2} y2={m.t + di * cellH + cellH / 2} /> <text class="cf-label" x={m.l - 10} y={m.t + di * cellH + cellH / 2 + 4} text-anchor="end">{d}</text> </g> ))} {hours.map((h, hi) => <text class="cf-label" x={m.l + hi * cellW + cellW / 2} y={H - 8} text-anchor="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 class="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} fill-opacity={0.35 + (v / max) * 0.65} />} </g> ); }))} </svg> </ChartFrame>

Información Rápida

Categoría
Gráficos y Estadísticas
Nombre de archivo
PunchCardChart.astro
Dependencias
apexcharts (peer)
Etiquetas
chartsvgzero-dependencypunch-cardactivitytime-of-daydots