{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "most-active-days-card",
  "title": "Most Active Days",
  "description": "Year calendar of per-day mini activity rings with a month jump control.",
  "dependencies": [
    "@internationalized/date@^3.12.3"
  ],
  "registryDependencies": [
    "https://components.exe.xyz/r/chart-card.json",
    "https://components.exe.xyz/r/select.json",
    "https://components.exe.xyz/r/utils.json"
  ],
  "files": [
    {
      "path": "src/components/charts/most-active-days-card.tsx",
      "content": "import { useMemo, useRef, useState } from \"react\"\nimport type { KeyboardEvent } from \"react\"\nimport { CalendarDate } from \"@internationalized/date\"\n\nimport {\n  ChartCard,\n  chartColor,\n  formatNumber,\n} from \"@/components/charts/chart-card\"\nimport { useCountUp } from \"@/lib/use-count-up\"\nimport { cn } from \"@/lib/utils\"\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\"\n\nexport type ActiveDay = {\n  date: string\n  rings: { progress: number }[]\n}\n\nconst MONTHS = [\n  \"January\",\n  \"February\",\n  \"March\",\n  \"April\",\n  \"May\",\n  \"June\",\n  \"July\",\n  \"August\",\n  \"September\",\n  \"October\",\n  \"November\",\n  \"December\",\n]\n\nfunction pad(n: number) {\n  return String(n).padStart(2, \"0\")\n}\n\nfunction isoFrom(date: CalendarDate) {\n  return `${date.year}-${pad(date.month)}-${pad(date.day)}`\n}\n\nfunction parseIso(iso: string) {\n  const [y, m, d] = iso.split(\"-\").map(Number)\n  return new CalendarDate(y, m, d)\n}\n\nfunction formatLong(iso: string) {\n  const date = parseIso(iso)\n  return `${MONTHS[date.month - 1]} ${date.day}, ${date.year}`\n}\n\nfunction weekday(date: CalendarDate) {\n  return date.toDate(\"UTC\").getUTCDay()\n}\n\nexport function demoActiveDays(year: number): ActiveDay[] {\n  const days: ActiveDay[] = []\n  let cursor = new CalendarDate(year, 1, 1)\n  const end = new CalendarDate(year, 12, 31)\n  while (cursor.compare(end) <= 0) {\n    const iso = isoFrom(cursor)\n    if (iso === `${year}-07-10`) {\n      days.push({\n        date: iso,\n        rings: [{ progress: 0.816 }, { progress: 0.84 }, { progress: 0.65 }],\n      })\n      cursor = cursor.add({ days: 1 })\n      continue\n    }\n    const seed = cursor.month * 17 + cursor.day * 13 + year\n    const weekend = weekday(cursor) === 0 || weekday(cursor) === 6\n    const quiet = seed % 11 === 0 || (weekend && seed % 3 === 0)\n    days.push({\n      date: iso,\n      rings: quiet\n        ? []\n        : [\n            { progress: Math.min(1, ((seed % 70) + 25) / 100) },\n            { progress: Math.min(1, ((seed % 55) + 30) / 100) },\n            { progress: Math.min(1, ((seed % 60) + 20) / 100) },\n          ],\n    })\n    cursor = cursor.add({ days: 1 })\n  }\n  return days\n}\n\nfunction MiniRings({ rings }: { rings: { progress: number }[] }) {\n  const cx = 10\n  const cy = 10\n  const radii = [8, 5.5, 3]\n  return (\n    <svg viewBox=\"0 0 20 20\" className=\"size-5\" aria-hidden>\n      {radii.map((r, index) => {\n        const circ = 2 * Math.PI * r\n        const progress = Math.min(rings[index]?.progress ?? 0, 1)\n        return (\n          <g key={r} transform={`rotate(-90 ${cx} ${cy})`}>\n            <circle\n              cx={cx}\n              cy={cy}\n              r={r}\n              fill=\"none\"\n              stroke=\"var(--muted)\"\n              strokeWidth={1.4}\n            />\n            {progress > 0 ? (\n              <circle\n                cx={cx}\n                cy={cy}\n                r={r}\n                fill=\"none\"\n                stroke={chartColor(index)}\n                strokeWidth={1.4}\n                strokeDasharray={circ}\n                strokeDashoffset={circ * (1 - progress)}\n              />\n            ) : null}\n          </g>\n        )\n      })}\n    </svg>\n  )\n}\n\nexport function MostActiveDaysCard({\n  year = 2026,\n  month,\n  headline = 32459,\n  headlineUnit = \"steps\",\n  days,\n  selectedDate,\n  onSelectDate,\n  className,\n}: {\n  year?: number\n  month?: number\n  headline?: number\n  headlineUnit?: string\n  days?: ActiveDay[]\n  selectedDate?: string\n  onSelectDate?: (isoDate: string) => void\n  className?: string\n}) {\n  const plotDays = days ?? demoActiveDays(year)\n  const byDate = useMemo(\n    () => new Map(plotDays.map((day) => [day.date, day])),\n    [plotDays]\n  )\n  const [internal, setInternal] = useState(selectedDate ?? `${year}-07-10`)\n  const selected = selectedDate ?? internal\n  const selectedMonth = parseIso(selected).month\n  const [jumpMonth, setJumpMonth] = useState(String(selectedMonth))\n  const monthRefs = useRef<Record<number, HTMLElement | null>>({})\n  const displayed = useCountUp(headline)\n  const summary = `${formatNumber(headline)} ${headlineUnit}. Selected ${formatLong(selected)}.`\n\n  function select(iso: string) {\n    if (selectedDate == null) setInternal(iso)\n    onSelectDate?.(iso)\n    setJumpMonth(String(parseIso(iso).month))\n  }\n\n  function onMonthJump(value: string) {\n    setJumpMonth(value)\n    const node = monthRefs.current[Number(value)]\n    node?.scrollIntoView({ block: \"nearest\" })\n    const first = plotDays.find(\n      (day) => parseIso(day.date).month === Number(value)\n    )\n    if (first) select(first.date)\n  }\n\n  function onMonthKey(event: KeyboardEvent<HTMLButtonElement>, iso: string) {\n    if (\n      event.key !== \"ArrowLeft\" &&\n      event.key !== \"ArrowRight\" &&\n      event.key !== \"ArrowUp\" &&\n      event.key !== \"ArrowDown\"\n    ) {\n      return\n    }\n    event.preventDefault()\n    const current = parseIso(iso)\n    const delta =\n      event.key === \"ArrowLeft\"\n        ? -1\n        : event.key === \"ArrowRight\"\n          ? 1\n          : event.key === \"ArrowUp\"\n            ? -7\n            : 7\n    const next = current.add({ days: delta })\n    if (next.year !== year || next.month !== current.month) return\n    select(isoFrom(next))\n  }\n\n  const months = month\n    ? [month]\n    : Array.from({ length: 12 }, (_, index) => index + 1)\n\n  return (\n    <ChartCard\n      title=\"Most active days\"\n      summary={summary}\n      className={className}\n      periodControl={\n        <Select value={jumpMonth} onValueChange={onMonthJump}>\n          <SelectTrigger\n            size=\"sm\"\n            aria-label=\"Jump to month\"\n            className=\"h-7 rounded-[2px] font-mono text-[11px]\"\n          >\n            <SelectValue />\n          </SelectTrigger>\n          <SelectContent align=\"end\">\n            {MONTHS.map((name, index) => (\n              <SelectItem key={name} value={String(index + 1)}>\n                {name}\n              </SelectItem>\n            ))}\n          </SelectContent>\n        </Select>\n      }\n    >\n      <div className=\"mb-2 flex items-baseline gap-2 px-1\">\n        <p\n          className=\"font-heading text-3xl font-semibold tracking-tight tabular-nums\"\n          aria-hidden\n        >\n          {formatNumber(displayed)}\n        </p>\n        <span className=\"text-sm text-muted-foreground\">{headlineUnit}</span>\n      </div>\n      <div className={month ? \"pr-1\" : \"max-h-64 overflow-auto pr-1\"}>\n        {months.map((monthNumber) => {\n          const first = new CalendarDate(year, monthNumber, 1)\n          const length = first.calendar.getDaysInMonth(first)\n          const padStart = weekday(first)\n          const cells: (CalendarDate | null)[] = [\n            ...Array.from({ length: padStart }, () => null),\n            ...Array.from(\n              { length },\n              (_, day) => new CalendarDate(year, monthNumber, day + 1)\n            ),\n          ]\n          return (\n            <section\n              key={monthNumber}\n              ref={(node) => {\n                monthRefs.current[monthNumber] = node\n              }}\n              className=\"mb-3 last:mb-0\"\n            >\n              <h3 className=\"mb-1 font-mono text-[11px] tracking-wide text-muted-foreground uppercase\">\n                {MONTHS[monthNumber - 1]}\n              </h3>\n              <div className=\"grid grid-cols-7 gap-0.5\">\n                {[\"S\", \"M\", \"T\", \"W\", \"T\", \"F\", \"S\"].map((day, index) => (\n                  <span\n                    key={`${day}-${index}`}\n                    className=\"text-center font-mono text-[9px] text-muted-foreground\"\n                  >\n                    {day}\n                  </span>\n                ))}\n                {cells.map((cell, index) => {\n                  if (!cell) {\n                    return <span key={`empty-${month}-${index}`} />\n                  }\n                  const iso = isoFrom(cell)\n                  const entry = byDate.get(iso)\n                  const rings = entry?.rings ?? []\n                  const pressed = selected === iso\n                  const kcal = Math.round((rings[0]?.progress ?? 0) * 1000)\n                  const label = rings.length\n                    ? `${formatLong(iso)}, ${kcal} kilocalories`\n                    : `${formatLong(iso)}, no activity`\n                  return (\n                    <button\n                      key={iso}\n                      type=\"button\"\n                      aria-label={label}\n                      aria-pressed={pressed}\n                      onClick={() => select(iso)}\n                      onKeyDown={(event) => onMonthKey(event, iso)}\n                      className={cn(\n                        \"flex flex-col items-center rounded-[2px] py-0.5 outline-none\",\n                        \"focus-visible:ring-2 focus-visible:ring-ring/50\",\n                        pressed && \"bg-muted\"\n                      )}\n                    >\n                      {rings.length > 0 ? (\n                        <MiniRings rings={rings} />\n                      ) : (\n                        <span className=\"size-5\" />\n                      )}\n                      <span className=\"font-mono text-[9px] leading-none text-muted-foreground\">\n                        {cell.day}\n                      </span>\n                    </button>\n                  )\n                })}\n              </div>\n            </section>\n          )\n        })}\n      </div>\n    </ChartCard>\n  )\n}\n\nexport { formatLong as formatActiveDate }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:block"
}