{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "waffle-chart",
  "title": "Waffle Chart",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/charts/waffle-chart/WaffleChart.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\nexport interface WaffleSlice {\n  name: string\n  value: number\n  color?: string\n}\n\ninterface Props {\n  data: WaffleSlice[]\n  height?: number | string\n  /** Cells per side (total = size²). Default 10. */\n  size?: number\n  /** Cell corner radius. Default 2. */\n  radius?: number\n  showLegend?: boolean\n  colors?: string[]\n  class?: HTMLAttributes['class']\n  /** Accessible name announced for the chart image. Defaults to \"Chart\". */\n  ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  height: 260,\n  size: 10,\n  radius: 2,\n  showLegend: true,\n  colors: () => ['var(--chart-1)', 'var(--chart-2)', 'var(--chart-3)', 'var(--chart-4)', 'var(--chart-5)'],\n})\n\nconst heightStyle = computed(() => (/^\\d+$/.test(String(props.height)) ? `${props.height}px` : String(props.height)))\nconst total = computed(() => props.data.reduce((s, d) => s + d.value, 0) || 1)\n\nconst cells = computed(() => {\n  const n = props.size * props.size\n  const counts = props.data.map((d) => Math.floor((d.value / total.value) * n))\n  let rest = n - counts.reduce((s, c) => s + c, 0)\n  const remainders = props.data\n    .map((d, i) => ({ i, r: (d.value / total.value) * n - counts[i]! }))\n    .sort((a, b) => b.r - a.r)\n  for (const { i } of remainders) {\n    if (rest <= 0) break\n    counts[i]!++\n    rest--\n  }\n  const out: { color: string; name: string }[] = []\n  props.data.forEach((d, i) => {\n    for (let k = 0; k < counts[i]!; k++)\n      out.push({ color: d.color ?? props.colors[i % props.colors.length]!, name: d.name })\n  })\n  return out.reverse()\n})\n</script>\n\n<template>\n  <div\n    data-slot=\"waffle-chart\"\n    role=\"img\"\n    tabindex=\"0\"\n    :aria-label=\"\n      ariaLabel || `Waffle chart: ${data.map((d) => `${d.name} ${Math.round((d.value / total) * 100)}%`).join(', ')}`\n    \"\n    :style=\"{ height: heightStyle }\"\n    :class=\"\n      cn(\n        'focus-visible:ring-ring flex w-full items-center justify-center gap-5 focus-visible:ring-2 focus-visible:outline-none',\n        props.class,\n      )\n    \"\n  >\n    <svg :viewBox=\"`0 0 ${size * 12} ${size * 12}`\" class=\"aspect-square h-full max-h-full\" role=\"presentation\">\n      <rect\n        v-for=\"(c, i) in cells\"\n        :key=\"i\"\n        :x=\"(i % size) * 12 + 1\"\n        :y=\"Math.floor(i / size) * 12 + 1\"\n        width=\"10\"\n        height=\"10\"\n        :rx=\"radius\"\n        :fill=\"c.color\"\n      >\n        <title>{{ c.name }}</title>\n      </rect>\n    </svg>\n    <ul v-if=\"showLegend\" class=\"space-y-1.5 text-xs\">\n      <li v-for=\"(d, i) in data\" :key=\"d.name\" class=\"flex items-center gap-2\">\n        <span class=\"size-2.5 rounded-[3px]\" :style=\"{ background: d.color ?? colors[i % colors.length] }\" />\n        <span class=\"text-foreground font-medium\">{{ d.name }}</span>\n        <span class=\"text-muted-foreground tabular-nums\">{{ Math.round((d.value / total) * 100) }}%</span>\n      </li>\n    </ul>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/charts/waffle-chart/WaffleChart.vue"
    },
    {
      "path": "packages/registry-vue/components/charts/waffle-chart/index.ts",
      "content": "export { default as WaffleChart } from './WaffleChart.vue'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/charts/waffle-chart/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Waffle chart as dependency-free SVG. 10×10 part-to-whole grid with legend. No extra npm packages.",
  "categories": [
    "chart"
  ]
}