{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "smooth-funnel",
  "title": "Smooth Funnel",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-vue/components/charts/smooth-funnel/SmoothFunnel.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport type { HTMLAttributes } from 'vue'\nimport { cn } from '@/lib/utils'\n\ninterface FunnelStage {\n  name: string\n  value: number\n  /** Optional override; defaults to chart-1..N from the registry palette. */\n  color?: string\n}\n\ninterface Props {\n  data: FunnelStage[]\n  /** Container height (px when numeric, raw CSS when string). Defaults to 240. */\n  height?: number | string\n  /** Show the percent pill on each stage. Defaults to true. */\n  showLabels?: boolean\n  /** Minimum segment height in px so tail stages stay visible at tiny percents. Default 18. */\n  minHeight?: number\n  /** Optional fallback palette when `color` is omitted on a stage. */\n  colors?: string[]\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  height: 240,\n  showLabels: true,\n  minHeight: 18,\n  colors: () => ['#3b82f6', '#a855f7', '#34d399', '#facc15', '#fb7185', '#06b6d4'],\n})\n\n// SVG geometry. Width/height are virtual (the SVG fits to container\n// via viewBox). The aspect ratio (W:H ≈ 4:1) matches the horizontal\n// \"flow\" layout consumers typically want for a 4-6 stage funnel; if\n// you need taller bands, override `height` and the curves stretch\n// vertically without distorting horizontally.\nconst W = 720\nconst H = 180\nconst cy = H / 2\n\nconst segments = computed(() => {\n  const stages = props.data\n  const n = stages.length\n  if (n === 0) return []\n  const segW = W / n\n  const max = Math.max(...stages.map((s) => s.value))\n  const pctOf = (v: number) => (max > 0 ? (v / max) * 100 : 0)\n  const heightFor = (pct: number) => Math.max((pct / 100) * H, props.minHeight)\n\n  return stages.map((s, i) => {\n    const next = stages[i + 1] ?? s\n    const startPct = pctOf(s.value)\n    const endPct = pctOf(next.value)\n    const h0 = heightFor(startPct)\n    const h1 = heightFor(endPct)\n    const x0 = i * segW\n    const x1 = x0 + segW\n    const yTop0 = cy - h0 / 2\n    const yTop1 = cy - h1 / 2\n    const yBot0 = cy + h0 / 2\n    const yBot1 = cy + h1 / 2\n\n    // Cubic bezier control points at 38% / 62% of segment width produce\n    // a soft S-curve transition between stages rather than the\n    // trapezoidal default of an ECharts funnel.\n    const cx1 = x0 + segW * 0.38\n    const cx2 = x0 + segW * 0.62\n\n    const d = [\n      `M ${x0.toFixed(1)} ${yTop0.toFixed(1)}`,\n      `C ${cx1.toFixed(1)} ${yTop0.toFixed(1)}, ${cx2.toFixed(1)} ${yTop1.toFixed(1)}, ${x1.toFixed(1)} ${yTop1.toFixed(1)}`,\n      `L ${x1.toFixed(1)} ${yBot1.toFixed(1)}`,\n      `C ${cx2.toFixed(1)} ${yBot1.toFixed(1)}, ${cx1.toFixed(1)} ${yBot0.toFixed(1)}, ${x0.toFixed(1)} ${yBot0.toFixed(1)}`,\n      'Z',\n    ].join(' ')\n\n    return {\n      d,\n      color: s.color ?? props.colors[i % props.colors.length],\n      percent: startPct,\n      name: s.name,\n      value: s.value,\n      labelX: x0 + segW * 0.42,\n      labelY: cy,\n    }\n  })\n})\n\nconst heightStyle = computed(() => (/^\\d+$/.test(String(props.height)) ? `${props.height}px` : String(props.height)))\n</script>\n\n<template>\n  <div\n    data-uipkge\n    data-slot=\"smooth-funnel\"\n    tabindex=\"0\"\n    :style=\"{ height: heightStyle }\"\n    :class=\"cn('focus-visible:ring-ring w-full focus-visible:ring-2 focus-visible:outline-none', props.class)\"\n  >\n    <svg :viewBox=\"`0 0 ${W} ${H}`\" class=\"block h-full w-full\" preserveAspectRatio=\"none\" role=\"img\">\n      <g v-for=\"(seg, i) in segments\" :key=\"i\">\n        <path :d=\"seg.d\" :fill=\"seg.color\" />\n        <foreignObject v-if=\"props.showLabels\" :x=\"seg.labelX - 28\" :y=\"seg.labelY - 12\" width=\"56\" height=\"24\">\n          <div\n            class=\"bg-background text-foreground inline-flex h-6 items-center rounded-full border px-2 text-[11px] font-semibold shadow-sm\"\n          >\n            {{ Math.round(seg.percent * 10) / 10 }}%\n          </div>\n        </foreignObject>\n      </g>\n    </svg>\n  </div>\n</template>\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/charts/smooth-funnel/SmoothFunnel.vue"
    },
    {
      "path": "packages/registry-vue/components/charts/smooth-funnel/index.ts",
      "content": "export { default as SmoothFunnel } from './SmoothFunnel.vue'\n",
      "type": "registry:ui",
      "target": "~/app/components/ui/charts/smooth-funnel/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Smoothly tapering SVG funnel with cubic-bezier transitions between stages. Pure SVG (no ECharts). Each stage is colored independently and shows its own percent pill; a minHeight floor keeps tail stages visible at tiny percents.",
  "categories": [
    "chart"
  ]
}