{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "funnel",
  "type": "registry:component",
  "title": "Funnel",
  "description": "How many make it to each step. Sample data: 48.2K site visits to 0.4K annual plans, 26% of visits sign up.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-funnel"
  ],
  "files": [
    {
      "path": "registry/chartory/components/funnel.tsx",
      "type": "registry:component",
      "target": "components/chartory/funnel.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Funnel (Chartory chart 032): sizes xs | s | m | l, laid out again from the data at each\n// size; collection \"hearth\" (default) or \"signal\" (with @chartory/signal, Code and Complete). Data in the type's shape (default: the sample). Docs:\n// https://usechartory.com/docs/charts/funnel\nimport { ChartView, type ChartViewProps } from \"@/lib/chartory/charts/chart-view\"\nimport { registerChart } from \"@/lib/chartory/charts/defs\"\nimport { data as sample } from \"@/lib/chartory/charts/types/032-funnel/data\"\nimport { def } from \"@/lib/chartory/charts/types/032-funnel/layout\"\n\nregisterChart(def, sample)\n\nexport type FunnelData = typeof sample\nexport type FunnelProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: FunnelData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: FunnelData[]\n}\n\nexport function Funnel(props: FunnelProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/032-funnel/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/032-funnel/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 032 Funnel · sample data (Figma chart/032, SaaS: September trial funnel, visit to annual plan).\nimport type { FunnelData } from \"../../plots/funnel\";\n\nexport const data: FunnelData = {\n  widths: [1, 0.84, 0.68, 0.54, 0.42, 0.34],\n  stages: [\n    {  label: \"Site visits\", text: \"48.2K\", tok: \"data/cat-2\"  },\n    {  label: \"Signups\", text: \"12.4K\", tok: \"data/cat-8\", conv: \"26%\"  },\n    {  label: \"Activated\", text: \"5.1K\", tok: \"data/cat-4\", conv: \"41%\"  },\n    {  label: \"Paid\", text: \"1.2K\", tok: \"data/cat-10\", conv: \"24%\"  },\n    {  label: \"Annual\", text: \"0.4K\", tok: \"data/cat-3\", conv: \"33%\"  },\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/032-funnel/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/032-funnel/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 032 Funnel (Figma chart/032): C.funnel; no legend (every stage is labelled).\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { funnel } from \"../../plots/funnel\";\nimport { data } from \"./data\";\n\nconst s = data.stages;\n\n/** Tooltip for one stage (group = the stage): its count and the step conversion from the stage before. */\nfunction tipOf(o: typeof data, g: number): TooltipSpec | null {\n  const st = o.stages[g];\n  if (!st) return null;\n  if (g === 0 || !st.conv) return { kind: \"compare\", caps: st.label.toUpperCase(), value: st.text, delta: { text: \"100%\", tone: \"neutral\" }, comparison: \"FUNNEL ENTRY\" };\n  return { kind: \"compare\", caps: st.label.toUpperCase(), value: st.text, delta: { text: st.conv, tone: \"info\" }, comparison: \"VS \" + o.stages[g - 1].label.toUpperCase() };\n}\n\nexport const def: ChartDef = {\n  no: 32,\n  slug: \"funnel\",\n  legend: null,\n  // update: the next month's funnel; visits move ±8 %, each step conversion ±3 points, every count and conversion is\n  // recomputed from the shown counts (one decimal in K) and the stage widths follow the counts a little\n  update: (d, step) => {\n    const o = d as typeof data;\n    const shown = (v: number) => Math.round(v / 100) / 10;\n    const n: number[] = [];\n    o.stages.forEach((s, i) => {\n      const base = parseFloat(s.text);\n      if (i === 0) { n.push(shown(base * 1000 * (1 + 0.08 * Math.sin(step * 2.2)))); return; }\n      const rate = base / parseFloat(o.stages[i - 1].text) + 0.03 * Math.sin(step * 1.6 + i * 1.9);\n      n.push(Math.max(0.1, shown(n[i - 1] * 1000 * rate)));\n    });\n    const widths = o.widths.map((w, i) => (i === 0 || i >= n.length ? w : +(w * (1 + 0.04 * Math.sin(step * 1.6 + i * 1.9))).toFixed(3)));\n    return {\n      ...o,\n      widths,\n      stages: o.stages.map((s, i) => ({ ...s, text: n[i].toFixed(1) + \"K\", conv: s.conv && Math.round((n[i] / n[i - 1]) * 100) + \"%\" })),\n    };\n  },\n  plot: (K, p, w, d) => funnel(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tipOf(d as typeof data, g),\n  describe: {\n    title: \"Trial funnel\",\n    summary: `September trial funnel: ${s[0].text} site visits, ${s[1].text} signups, ${s[2].text} activated, ${s[3].text} paid and ${s[4].text} on an annual plan.`,\n    table: { head: [\"Stage\", \"Count\", \"Step conversion\"], rows: s.map((x) => [x.label, x.text, x.conv ?? \"\"]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "flow-between-steps"
  ],
  "meta": {
    "access": "free",
    "no": 32,
    "family": "Flow between steps"
  }
}
