{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "github-contributions",
  "title": "GitHub Contributions",
  "description": "A basic GitHub contributions component.",
  "files": [
    {
      "path": "registry/github-contributions/github-contributions.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n// Constants\nconst WEEKDAYS = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"] as const;\nconst CONTRIBUTION_LEVEL_CLASSES = [\n  \"bg-github-0\",\n  \"bg-github-1\",\n  \"bg-github-2\",\n  \"bg-github-3\",\n  \"bg-github-4\",\n] as const;\n\n// Types\ntype ContributionLevel = 0 | 1 | 2 | 3 | 4;\n\nexport interface Contribution {\n  date: string;\n  count: number;\n  level: ContributionLevel;\n}\n\nexport interface DayContributionMap {\n  [key: number]: Contribution[];\n}\n\nexport interface ContributionCellProps {\n  contribution: Contribution;\n}\n\nconst ContributionCell = React.memo(function ContributionCell({\n  contribution,\n}: ContributionCellProps) {\n  const date = new Date(contribution.date);\n  const formattedDate = date.toLocaleString(\"en-US\", {\n    month: \"short\",\n    day: \"numeric\",\n  });\n  const label = `${contribution.count} contributions on ${formattedDate}`;\n\n  return (\n    <td\n      role=\"gridcell\"\n      aria-label={label}\n      className={cn(\n        \"w-2.5 p-0 rounded-xs -outline-offset-1\",\n        CONTRIBUTION_LEVEL_CLASSES[contribution.level],\n      )}\n      title={label}\n    />\n  );\n});\n\nconst WeekdayLabel = React.memo(function WeekdayLabel({\n  day,\n}: {\n  day: number;\n}) {\n  return (\n    <td rowSpan={2} className=\"text-xs font-semibold pr-6 sm:pr-8 relative\">\n      <div className=\"absolute -top-1 -left-1\">{WEEKDAYS[day]}</div>\n    </td>\n  );\n});\n\nconst MonthLabel = React.memo(function MonthLabel({\n  date,\n  colSpan,\n}: {\n  date?: Date;\n  colSpan: number;\n}) {\n  if (!date) return <td colSpan={colSpan} />;\n  return (\n    <td\n      colSpan={colSpan}\n      className=\"text-xs font-semibold first-letter:uppercase\"\n    >\n      {date.toLocaleString(\"en-US\", { month: \"short\" })}\n    </td>\n  );\n});\n\nexport default function GitHubContributions({\n  data,\n}: {\n  data: Contribution[];\n}): React.JSX.Element {\n  const contributionsByDay = React.useMemo(() => {\n    return data.reduce<DayContributionMap>((acc, contribution) => {\n      const day = new Date(contribution.date).getDay();\n      if (!acc[day]) acc[day] = [];\n      acc[day].push(contribution);\n      return acc;\n    }, {});\n  }, [data]);\n\n  const [lastWeekCutoff] = React.useState(\n    () => new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),\n  );\n\n  const monthLabels = React.useMemo(\n    () =>\n      data.reduce<(React.JSX.Element | null)[]>((acc, contribution, i) => {\n        const date = new Date(contribution.date);\n        const isStartOfWeek = i % 7 === 0;\n        const isWithinFirstWeekOfMonth =\n          date.getDate() >= 1 && date.getDate() <= 7;\n        const isBeforeLastWeek = date < lastWeekCutoff;\n\n        if (\n          i === 0 ||\n          (isStartOfWeek && isWithinFirstWeekOfMonth && isBeforeLastWeek)\n        ) {\n          const daysInMonth = new Date(\n            date.getFullYear(),\n            date.getMonth() + 1,\n            0,\n          ).getDate();\n          const remainingDays = daysInMonth - date.getDate();\n\n          acc[i] = (\n            <MonthLabel\n              key={i}\n              date={remainingDays > 14 ? date : undefined}\n              colSpan={\n                i === 0\n                  ? Math.ceil(remainingDays / 7)\n                  : remainingDays >= 28\n                    ? 5\n                    : 4\n              }\n            />\n          );\n        } else {\n          acc[i] = null;\n        }\n        return acc;\n      }, []),\n    [data, lastWeekCutoff],\n  );\n\n  const renderRow = React.useCallback(\n    (dayNum: number) => (\n      <tr key={dayNum} className=\"h-2.5\" role=\"row\">\n        {dayNum === 0 && <td />}\n        {dayNum % 2 === 1 && <WeekdayLabel day={dayNum} />}\n        {contributionsByDay[dayNum]?.map((contribution, index) => (\n          <ContributionCell key={index} contribution={contribution} />\n        ))}\n      </tr>\n    ),\n    [contributionsByDay],\n  );\n\n  return (\n    <div\n      className=\"overflow-x-auto max-w-full\"\n      role=\"region\"\n      aria-label=\"GitHub contributions calendar\"\n    >\n      <table\n        className=\"border-separate border-spacing-0.5 w-max mx-auto px-2 text-foreground\"\n        role=\"grid\"\n      >\n        <thead>\n          <tr role=\"row\">\n            <td />\n            {monthLabels}\n          </tr>\n        </thead>\n        <tbody>{Array.from({ length: 7 }, (_, i) => renderRow(i))}</tbody>\n      </table>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "cssVars": {
    "theme": {
      "--color-github-0": "var(--muted)"
    },
    "light": {
      "github-1": "oklch(0.87 0.1182 148.74)",
      "github-2": "oklch(0.73 0.1781 148.54)",
      "github-3": "oklch(0.63 0.157 148.35)",
      "github-4": "oklch(0.48 0.1122 150.02)"
    },
    "dark": {
      "github-1": "oklch(0.34 0.073 156.73)",
      "github-2": "oklch(0.47 0.126255 150.8624)",
      "github-3": "oklch(0.64 0.1777 146.24)",
      "github-4": "oklch(0.76 0.2121 145.62)"
    }
  },
  "type": "registry:block"
}