{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "author": "Lopifi (https://lopifi.com)",
  "categories": [
    "dashboard-charts",
    "finance",
    "accounting"
  ],
  "dependencies": [],
  "description": "Ranked bars, not a pie.",
  "files": [
    {
      "content": "\"use client\";\n\nimport { allocatePercentages } from \"@/lib/ledger/allocate\";\nimport { vizColorFor } from \"@/lib/ledger/chart-utils\";\nimport { cn } from \"@/lib/ledger/cn\";\nimport type { MoneyValue } from \"@/lib/ledger/money.types\";\nimport { Money } from \"@/components/ledger/money\";\nimport { Percent } from \"@/components/ledger/percent\";\n\nexport type Category = { key: string; label: string; amount: number };\n\nexport interface CategoryBreakdownProps {\n  categories: Category[];\n  className?: string;\n  currency: string;\n  /** Max 6 series; the rest group into \"Other\". Beyond that is a data problem. */\n  maxSeries?: number;\n  title?: string;\n}\n\n/**\n * Ranked horizontal bars. **Not a pie chart** — humans compare lengths\n * accurately and angles poorly.\n *\n * Percentages use largest-remainder allocation, so they sum to exactly 100%.\n */\nexport function CategoryBreakdown({\n  categories,\n  currency,\n  maxSeries = 6,\n  className,\n  title,\n}: CategoryBreakdownProps) {\n  const sorted = [...categories].sort((a, b) => b.amount - a.amount);\n  const top = sorted.slice(0, maxSeries - (sorted.length > maxSeries ? 1 : 0));\n  const rest = sorted.slice(top.length);\n  const rows =\n    rest.length > 0\n      ? [...top, { amount: rest.reduce((s, c) => s + c.amount, 0), key: \"other\", label: \"Other\" }]\n      : top;\n\n  const percentages = allocatePercentages(rows.map((r) => r.amount));\n  const max = Math.max(...rows.map((r) => r.amount), 1);\n\n  return (\n    <div className={cn(\"w-full\", className)}>\n      <table className=\"w-full\">\n        {title ? <caption className=\"sr-only\">{title}</caption> : null}\n        <tbody>\n          {rows.map((row, i) => (\n            <tr key={row.key}>\n              <th className=\"w-[35%] max-w-0 truncate py-2 pr-3 text-left text-body font-normal\" scope=\"row\">\n                {row.label}\n              </th>\n              <td className=\"py-2\">\n                <div\n                  aria-hidden=\"true\"\n                  className=\"h-4 rounded-xs\"\n                  style={{\n                    background: vizColorFor(row.key),\n                    width: `${(row.amount / max) * 100}%`,\n                  }}\n                />\n              </td>\n              <td className=\"w-24 py-2 pl-3 text-right\">\n                <Money value={{ amount: row.amount, currency } as MoneyValue} />\n              </td>\n              <td className=\"w-16 py-2 pl-3 text-right\">\n                <Percent\n                  className=\"text-text-secondary\"\n                  signed={false}\n                  value={{ kind: \"relative\", value: percentages[i] }}\n                />\n              </td>\n            </tr>\n          ))}\n        </tbody>\n      </table>\n      <p className=\"mt-2 text-caption text-text-tertiary\">\n        Shares allocated by largest remainder, so they sum to exactly 100%.\n      </p>\n    </div>\n  );\n}\n",
      "path": "components/ledger/category-breakdown.tsx",
      "target": "components/ledger/category-breakdown.tsx",
      "type": "registry:component"
    }
  ],
  "name": "category-breakdown",
  "registryDependencies": [
    "@lopifi/theme",
    "@lopifi/allocate",
    "@lopifi/chart-utils",
    "@lopifi/cn",
    "@lopifi/money",
    "@lopifi/money.types",
    "@lopifi/percent"
  ],
  "title": "CategoryBreakdown",
  "type": "registry:component"
}
