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

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

Bump Chart

График изменения рейтинга во времени с пронумерованными узлами позиций для каждой сущности.

Живое превью

Framework popularity rank

Developer survey position by year

  • Astro
  • Next
  • Svelte
  • Nuxt
  • Remix
202120222023202420252026543211AstroAstro111122NextNext322334SvelteSvelte234453NuxtNuxt455545RemixRemix
View data
Name202120222023202420252026
Astro#5#4#3#2#1#1
Next#1#1#1#1#2#2
Svelte#3#2#2#3#3#4
Nuxt#2#3#4#4#5#3
Remix#4#5#5#5#4#5

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

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

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

<BumpChart periods={['2024', '2025', '2026']} series={[
    { name: 'React', ranks: [3, 2, 1] },
    { name: 'Next', ranks: [1, 1, 2] },
    { name: 'Svelte', ranks: [2, 3, 3] },
  ]} />

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

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

BumpChart.tsx
'use client';

import ChartFrame from './ChartFrame';
import { colorAt, smoothPath, type Point } from './chart-utils';

/**
 * BumpChart — Rank-over-time chart. Each line tracks one entity's position
 * (1 = top) across periods, with labels at both ends.
 *
 * @param {string[]} periods - X-axis labels.
 * @param {Array<{name: string, ranks: number[]}>} series - Rank per period (1-based), max 8.
 * @param {string[]} colors - Optional per-series colors.
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
export interface BumpChartProps {
  title?: string;
  subtitle?: string;
  periods?: string[];
  series?: Array<{ name: string; ranks: number[] }>;
  colors?: string[];
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

export function BumpChart({
  title = 'Framework popularity rank',
  subtitle = 'Developer survey position by year',
  periods = ['2021', '2022', '2023', '2024', '2025', '2026'],
  series = [
    { name: 'Astro', ranks: [5, 4, 3, 2, 1, 1] },
    { name: 'Next', ranks: [1, 1, 1, 1, 2, 2] },
    { name: 'Svelte', ranks: [3, 2, 2, 3, 3, 4] },
    { name: 'Nuxt', ranks: [2, 3, 4, 4, 5, 3] },
    { name: 'Remix', ranks: [4, 5, 5, 5, 4, 5] },
  ],
  colors,
  variant = 'glass',
  size = 'md',
  className = '',
}: BumpChartProps) {
  const W = 640;
  const maxRank = Math.max(...series.flatMap((s) => s.ranks));
  const rowH = 44;
  const H = maxRank * rowH + 36;
  const m = { t: 30, l: 80, r: 80 };
  const n = periods.length;
  const x = (i: number) => m.l + (i / Math.max(1, n - 1)) * (W - m.l - m.r);
  const y = (r: number) => m.t + (r - 1) * rowH + 8;
  const lines = series.slice(0, 8).map((s, si) => ({
    ...s,
    color: colorAt(si, colors),
    pts: s.ranks.map((r, i): Point => [x(i), y(r)]),
  }));
  return (
    <>
      <ChartFrame title={title} subtitle={subtitle} variant={variant} size={size} className={className} legend={lines.map((l) => ({ label: l.name, color: l.color }))} table={{ headers: ['Name', ...periods], rows: series.map((s) => [s.name, ...s.ranks.map((r) => `#${r}`)]) }}>
        <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: bump chart of ranks over ${n} periods`}>
          {periods.map((p, i) => (
            <g key={i}>
              <line className="cf-grid" x1={x(i)} x2={x(i)} y1={m.t - 6} y2={H - 10} />
              <text className="cf-label" x={x(i)} y={14} textAnchor="middle">{p}</text>
            </g>
          ))}
          {lines.map((l, __i: number) => (
            <g key={__i} className="cf-mark cf-dim" tabIndex={0} role="img" aria-label={`${l.name}: ${l.ranks.map((r, i) => `${periods[i]} #${r}`).join(', ')}`} data-tip-title={l.name} data-tip={l.ranks.map((r, i) => `${periods[i]}: #${r}`).join('|')}>
              <path d={smoothPath(l.pts)} fill="none" stroke={l.color} strokeWidth="3" strokeLinecap="round" />
              <path d={smoothPath(l.pts)} fill="none" stroke="transparent" strokeWidth="16" />
              {l.pts.map(([px, py], i) => (
                <g key={i}>
                  <circle cx={px} cy={py} r="11" fill={l.color} stroke="var(--chart-surface, #0d0d0f)" strokeWidth="2" />
                  <text x={px} y={py + 4} textAnchor="middle" fontSize="10" fontWeight="700" fill="#fff">{l.ranks[i]}</text>
                </g>
              ))}
              <text className="cf-label cf-label--strong" x={m.l - 18} y={l.pts[0][1] + 4} textAnchor="end">{l.name}</text>
              <text className="cf-label cf-label--strong" x={W - m.r + 18} y={l.pts[n - 1][1] + 4}>{l.name}</text>
            </g>
          ))}
        </svg>
      </ChartFrame>
    </>
  );
}

export default BumpChart;

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

Категория
Графики и статистика
Имя файла
BumpChart.tsx
Зависимости
Нет — чистый React + CSS
Теги
chartsvgzero-dependencyrankbumpleaderboardtime-series