{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "block-timeline",
  "type": "registry:component",
  "title": "Block timeline",
  "description": "Each team's items and their status. Sample data: items across core, growth, platform and mobile squads, shipped, in progress, blocked and planned.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-blocks"
  ],
  "files": [
    {
      "path": "registry/chartory/components/block-timeline.tsx",
      "type": "registry:component",
      "target": "components/chartory/block-timeline.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Block timeline (Chartory chart 038): 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/block-timeline\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/038-block-timeline/data\"\nimport { def } from \"@/lib/chartory/charts/types/038-block-timeline/layout\"\n\nregisterChart(def, sample)\n\nexport type BlockTimelineData = typeof sample\nexport type BlockTimelineProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: BlockTimelineData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: BlockTimelineData[]\n}\n\nexport function BlockTimeline(props: BlockTimelineProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/038-block-timeline/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/038-block-timeline/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 038 Block timeline · sample data (Figma chart/038, SaaS: Q4 release train per squad; [weight, state, item, status]).\nimport type { BlocksData } from \"../../plots/blocks\";\n\nexport const data: BlocksData = {\n  cols: [\n    { name: \"CORE\", blocks: [[3, \"status/success\", \"Billing v3\", \"SHIPPED\"], [2, \"data/cat-2\", \"Usage alerts\", \"4 DAYS LEFT\"], [2, \"data/track\", \"Audit log\", \"PLANNED\"]] },\n    { name: \"GROWTH\", blocks: [[2, \"status/success\", \"Trial nudges\", \"SHIPPED\"], [3, \"data/cat-2\", \"Pricing page\", \"6 DAYS LEFT\"], [2, \"status/danger\", \"Referral v2\", \"BLOCKED\"]] },\n    { name: \"PLATFORM\", blocks: [[4, \"data/cat-2\", \"SSO for teams\", \"9 DAYS LEFT\"], [3, \"data/track\", \"Rate limits\", \"PLANNED\"]] },\n    { name: \"MOBILE\", blocks: [[2, \"status/danger\", \"Push crash\", \"BLOCKED\"], [2, \"status/success\", \"Offline mode\", \"SHIPPED\"], [3, \"data/track\", \"Widgets\", \"PLANNED\"]] },\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/038-block-timeline/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/038-block-timeline/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 038 Block timeline (Figma chart/038): C.blocks on the Q4 release train sample.\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { blocks } from \"../../plots/blocks\";\nimport { data } from \"./data\";\n\nconst legend: [string, string][] = [[\"status/success\", \"Shipped\"], [\"data/cat-2\", \"In progress\"], [\"status/danger\", \"Blocked\"], [\"data/track\", \"Planned\"]];\nconst all = data.cols.flatMap((c) => c.blocks.map((b) => ({ squad: c.name, b })));\nconst count = (tok: string) => all.filter((x) => x.b[1] === tok).length;\nconst cap = (s: string) => s.charAt(0) + s.slice(1).toLowerCase();\n\n/** Tooltip for one block (group = squad × 10 + the block's place in the squad): the item, its state and status. */\nfunction tipOf(o: typeof data, g: number): TooltipSpec | null {\n  const col = o.cols[Math.floor(g / 10)], b = col?.blocks[g % 10];\n  if (!b) return null;\n  const st = (legend.find((l) => l[0] === b[1])?.[1] ?? \"\").toUpperCase();\n  return { kind: \"cell\", caps: col.name + \" · Q4\", value: b[2], swatch: b[1], share: st + (b[3] && b[3] !== st ? \" · \" + b[3] : \"\") };\n}\n\nexport const def: ChartDef = {\n  no: 38,\n  slug: \"block-timeline\",\n  legend,\n  // update: days later in the quarter; items in progress count their days down (one a step). Block sizes stay: they\n  // sit on the title / status-line thresholds (22 / 42 px) at several sizes, so a re-estimate would add or drop text\n  update: (d, step) => {\n    const o = d as typeof data;\n    const left = (c?: string) => {\n      const m = c?.match(/^(\\d+) DAYS? LEFT$/);\n      if (!m) return c;\n      const n = Math.max(1, +m[1] - step);\n      return n + (n === 1 ? \" DAY LEFT\" : \" DAYS LEFT\");\n    };\n    return { ...o, cols: o.cols.map((c) => ({ ...c, blocks: c.blocks.map(([w, tok, title, caps]) => [w, tok, title, left(caps)] as typeof c.blocks[number]) })) };\n  },\n  plot: (K, p, w, d) => blocks(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tipOf(d as typeof data, g),\n  describe: {\n    title: \"Release train · Q4\",\n    summary: `${all.length} Q4 items across ${data.cols.length} squads: ${count(\"status/success\")} shipped, ${count(\"data/cat-2\")} in progress, ${count(\"status/danger\")} blocked, ${count(\"data/track\")} planned.`,\n    table: { head: [\"Squad\", \"Item\", \"State\", \"Status\"], rows: all.map(({ squad, b }) => [cap(squad), b[2], legend.find((l) => l[0] === b[1])![1], cap(b[3] ?? \"\")]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "schedule-and-status"
  ],
  "meta": {
    "access": "free",
    "no": 38,
    "family": "Schedule and status"
  }
}
