44 lines · 1.4 KB
import { cn } from "~/utils/cn";

type Day = { date: string; count: number };

const WEEK = 7;
const LEVELS = ["bg-gray-900", "bg-orange-950", "bg-orange-800", "bg-orange-600", "bg-orange-400"];
const LEVEL_STEPS = [0, 1, 3, 6, 10];

function level(count: number): string {
  let index = 0;
  for (const [step, threshold] of LEVEL_STEPS.entries()) if (count >= threshold) index = step;
  return LEVELS[index];
}

export function ContributionGraph({ days }: { days: Day[] }) {
  if (days.length === 0) return null;
  const weeks: Day[][] = [];
  for (let index = 0; index < days.length; index += WEEK) weeks.push(days.slice(index, index + WEEK));
  const total = days.reduce((sum, day) => sum + day.count, 0);

  return (
    <section className="space-y-2">
      <h2 className="label-caps text-gray-500">
        {total} contribution{total === 1 ? "" : "s"} in the last year
      </h2>
      <div className="overflow-x-auto rounded-md border border-gray-800 bg-gray-950/60 p-3">
        <div className="flex gap-[3px]" aria-hidden>
          {weeks.map((week) => (
            <div key={week[0].date} className="flex flex-col gap-[3px]">
              {week.map((day) => (
                <span
                  key={day.date}
                  title={`${day.count} on ${day.date}`}
                  className={cn("size-2.5 rounded-[2px]", level(day.count))}
                />
              ))}
            </div>
          ))}
        </div>
      </div>