{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "smooth-funnel",
  "title": "Smooth Funnel",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-react/components/charts/smooth-funnel/SmoothFunnel.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\n\nfunction heightToStyle(height: number | string): string {\n  return /^\\d+$/.test(String(height)) ? `${height}px` : String(height)\n}\n\n// ─────────────────────────────────────────────────────────────────────────\n// SmoothFunnel — pure SVG, no ECharts\n// ─────────────────────────────────────────────────────────────────────────\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\nexport interface SmoothFunnelProps {\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  className?: string\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 SF_W = 720\nconst SF_H = 180\nconst SF_CY = SF_H / 2\n\nexport const SmoothFunnel = React.forwardRef<HTMLDivElement, SmoothFunnelProps>(\n  (\n    {\n      data,\n      height = 240,\n      showLabels = true,\n      minHeight = 18,\n      colors = ['#3b82f6', '#a855f7', '#34d399', '#facc15', '#fb7185', '#06b6d4'],\n      className,\n    },\n    ref,\n  ) => {\n    const segments = React.useMemo(() => {\n      const stages = data\n      const n = stages.length\n      if (n === 0) return []\n      const segW = SF_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) * SF_H, 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 = SF_CY - h0 / 2\n        const yTop1 = SF_CY - h1 / 2\n        const yBot0 = SF_CY + h0 / 2\n        const yBot1 = SF_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 ?? colors[i % colors.length],\n          percent: startPct,\n          name: s.name,\n          value: s.value,\n          labelX: x0 + segW * 0.42,\n          labelY: SF_CY,\n        }\n      })\n    }, [data, minHeight, colors])\n\n    return (\n      <div\n        ref={ref}\n        data-uipkge=\"\"\n        data-slot=\"smooth-funnel\"\n        tabIndex={0}\n        style={{ height: heightToStyle(height) }}\n        className={cn('focus-visible:ring-ring w-full focus-visible:ring-2 focus-visible:outline-none', className)}\n      >\n        <svg viewBox={`0 0 ${SF_W} ${SF_H}`} className=\"block h-full w-full\" preserveAspectRatio=\"none\" role=\"img\">\n          {segments.map((seg, i) => (\n            <g key={i}>\n              <path d={seg.d} fill={seg.color} />\n              {showLabels && (\n                <foreignObject x={seg.labelX - 28} y={seg.labelY - 12} width={56} height={24}>\n                  <div className=\"bg-background text-foreground inline-flex h-6 items-center rounded-full border px-2 text-[11px] font-semibold shadow-sm\">\n                    {Math.round(seg.percent * 10) / 10}%\n                  </div>\n                </foreignObject>\n              )}\n            </g>\n          ))}\n        </svg>\n      </div>\n    )\n  },\n)\nSmoothFunnel.displayName = 'SmoothFunnel'\n",
      "type": "registry:ui",
      "target": "~/components/ui/charts/smooth-funnel/SmoothFunnel.tsx"
    },
    {
      "path": "packages/registry-react/components/charts/smooth-funnel/index.ts",
      "content": "export { SmoothFunnel, type SmoothFunnelProps } from './SmoothFunnel'\n",
      "type": "registry:ui",
      "target": "~/components/ui/charts/smooth-funnel/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "React mirror of @uipkge/smooth-funnel.",
  "categories": [
    "chart"
  ]
}