{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tree",
  "type": "registry:component",
  "title": "Tree",
  "description": "A release, its epics and their tasks. Sample data: release v4.2 (Dec 05), its epics and their tasks, each with a status colour (shipped, in progress, review, todo, blocked).",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-tree2"
  ],
  "files": [
    {
      "path": "registry/chartory/components/tree.tsx",
      "type": "registry:component",
      "target": "components/chartory/tree.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Tree (Chartory chart 040): 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/tree\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/040-tree/data\"\nimport { def } from \"@/lib/chartory/charts/types/040-tree/layout\"\n\nregisterChart(def, sample)\n\nexport type TreeData = typeof sample\nexport type TreeProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: TreeData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: TreeData[]\n}\n\nexport function Tree(props: TreeProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/040-tree/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/040-tree/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 040 Tree · sample data (Figma chart/040, project management: release v4.2 → epics → tasks with their status).\nimport type { Tree2Data } from \"../../plots/tree2\";\n\nexport const data: Tree2Data = {\n  heads: [\"RELEASE\", \"EPICS\", \"TASKS\"],\n  levels: [\n    [{ label: \"v4.2 · Dec 05\", status: \"release\" }],\n    [\n      { label: \"Billing v3\", status: \"shipped\", parent: 0 },\n      { label: \"SSO for teams\", status: \"in progress\", parent: 0 },\n      { label: \"Referral v2\", status: \"blocked\", parent: 0 },\n    ],\n    [\n      { label: \"Invoices\", status: \"shipped\", parent: 0 },\n      { label: \"SAML login\", status: \"review\", parent: 1 },\n      { label: \"SCIM sync\", status: \"todo\", parent: 1 },\n      { label: \"Legal review\", status: \"blocked\", parent: 2 },\n    ],\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/040-tree/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/040-tree/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 040 Tree (Figma chart/040): C.tree2; no legend (each card names its status).\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { tree2 } from \"../../plots/tree2\";\nimport { data } from \"./data\";\n\nconst [rel, epics, tasks] = data.levels;\nconst items = data.levels.flat();\nconst status = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);\n\nconst TONE: Record<string, string> = { shipped: \"status/success\", \"in progress\": \"status/info\", review: \"status/warning\", blocked: \"status/danger\", todo: \"text/unselected\", release: \"data/primary\" };\n\n/** Tooltip for one item (group = the card with its link in, numbered level by level): level and parent, the item,\n *  its status and how many items hang under it. */\nfunction tipOf(o: typeof data, g: number): TooltipSpec | null {\n  let k = 0;\n  for (let l = 0; l < o.levels.length; l++)\n    for (let i = 0; i < o.levels[l].length; i++, k++) {\n      if (k !== g) continue;\n      const it = o.levels[l][i], par = it.parent != null && l > 0 ? o.levels[l - 1][it.parent] : null;\n      const kids = l + 1 < o.levels.length ? o.levels[l + 1].filter((c) => c.parent === i).length : 0;\n      return {\n        kind: \"cell\", caps: o.heads[l] + (par ? \" · \" + par.label.toUpperCase() : \"\"), value: it.label, swatch: TONE[it.status] ?? \"text/unselected\",\n        share: it.status.toUpperCase() + (kids ? ` · ${kids} ${o.heads[l + 1]}` : \"\"),\n      };\n    }\n  return null;\n}\n\nexport const def: ChartDef = {\n  no: 40,\n  slug: \"tree\",\n  legend: null,\n  // update: the release a few days on; work moves one status forward per step (todo → in progress → review →\n  // shipped), blocked items stay blocked and an epic ships once all its tasks have; cards keep their size and place\n  update: (d, step) => {\n    const o = d as typeof data;\n    const flow = [\"todo\", \"in progress\", \"review\", \"shipped\"];\n    const fwd = (s: string) => (flow.includes(s) ? flow[Math.min(flow.length - 1, flow.indexOf(s) + step)] : s);\n    const tasks = o.levels[2].map((t) => ({ ...t, status: fwd(t.status) }));\n    const epics = o.levels[1].map((e, i) => {\n      const kids = tasks.filter((t) => t.parent === i);\n      return { ...e, status: e.status === \"blocked\" || e.status === \"shipped\" ? e.status : kids.every((t) => t.status === \"shipped\") ? \"shipped\" : kids.some((t) => t.status !== \"todo\") ? \"in progress\" : e.status };\n    });\n    return { ...o, levels: [o.levels[0], epics, tasks] };\n  },\n  plot: (K, p, w, d) => tree2(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tipOf(d as typeof data, g),\n  describe: {\n    title: \"Release dependencies\",\n    summary: `Release ${rel[0].label} depends on ${epics.length} epics and ${tasks.length} tasks; ${items.filter((x) => x.status === \"blocked\").length} items are blocked and ${items.filter((x) => x.status === \"shipped\").length} have shipped.`,\n    table: {\n      head: [\"Item\", \"Level\", \"Parent\", \"Status\"],\n      rows: data.levels.flatMap((lv, l) => lv.map((it) => [it.label, data.heads[l].charAt(0) + data.heads[l].slice(1).toLowerCase(), it.parent != null ? data.levels[l - 1][it.parent].label : \"–\", status(it.status)])),\n    },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "relationship"
  ],
  "meta": {
    "access": "free",
    "no": 40,
    "family": "Relationship"
  }
}
