{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cluster-map",
  "type": "registry:component",
  "title": "Cluster map",
  "description": "Similar items grouped into named clusters. Sample data: support tickets in five clusters, billing, login and SSO, integrations, bugs and feature asks.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-clusters"
  ],
  "files": [
    {
      "path": "registry/chartory/components/cluster-map.tsx",
      "type": "registry:component",
      "target": "components/chartory/cluster-map.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Cluster map (Chartory chart 091): 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/cluster-map\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/091-cluster-map/data\"\nimport { def } from \"@/lib/chartory/charts/types/091-cluster-map/layout\"\n\nregisterChart(def, sample)\n\nexport type ClusterMapData = typeof sample\nexport type ClusterMapProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: ClusterMapData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: ClusterMapData[]\n}\n\nexport function ClusterMap(props: ClusterMapProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/091-cluster-map/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/091-cluster-map/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 091 Cluster map · sample data (Figma chart/091-clusters-tickets, SaaS: support tickets embedded and clustered, last 7 days).\nimport type { ClustersData } from \"../../plots/clusters\";\n\nexport const data: ClustersData = {\n  seed: 91,\n  clusters: [\n    { label: \"BILLING\", tok: \"data/cat-2\", cx: 0.18, cy: 0.36, sx: 30, sy: 18, n: 22 },\n    { label: \"LOGIN AND SSO\", tok: \"data/primary\", cx: 0.5, cy: 0.3, sx: 34, sy: 20, n: 26 },\n    { label: \"INTEGRATIONS\", tok: \"data/cat-3\", cx: 0.82, cy: 0.4, sx: 28, sy: 18, n: 18 },\n    { label: \"BUGS\", tok: \"status/danger\", cx: 0.32, cy: 0.76, sx: 26, sy: 14, n: 16 },\n    { label: \"FEATURE ASKS\", tok: \"data/cat-5\", cx: 0.7, cy: 0.78, sx: 30, sy: 14, n: 12 },\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/091-cluster-map/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/091-cluster-map/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 091 Cluster map (Figma chart/091-clusters-tickets): C.clusters; no legend (each cluster is named on the map).\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { clusters } from \"../../plots/clusters\";\nimport { data } from \"./data\";\n\nconst name = (s: string) => s.charAt(0) + s.slice(1).toLowerCase();\n\n/** The next 7 days: same ticket sample, the topics shift and spread a little (same seed, so points travel with them). */\nfunction update(d0: unknown, step: number): typeof data {\n  const d = d0 as typeof data;\n  const s = (i: number, k: number) => Math.sin(i * 2.1 + k * 1.3 + step * 1.7);\n  return { ...d, clusters: d.clusters.map((c, i) => ({ ...c, cx: +(c.cx + 0.025 * s(i, 0)).toFixed(3), cy: +(c.cy + 0.03 * s(i, 1)).toFixed(3), sx: Math.round(c.sx * (1 + 0.12 * s(i, 2))), sy: Math.round(c.sy * (1 + 0.12 * s(i, 3))) })) };\n}\n\n/** Hover: one topic cluster (hull, outline, points, name): tickets shown and their share of the sample. */\nfunction tip(d0: unknown, g: number): TooltipSpec | null {\n  const cs = (d0 as typeof data).clusters, c = cs[g];\n  if (!c) return null;\n  const all = cs.reduce((a, x) => a + x.n, 0);\n  return { kind: \"value\", text: `${name(c.label)} · ${c.n} tickets · ${Math.round((100 * c.n) / all)}%` };\n}\n\nexport const def: ChartDef = {\n  no: 91,\n  slug: \"cluster-map\",\n  legend: null,\n  plot: (K, p, w, d) => clusters(K, p, w, (d as typeof data | undefined) ?? data),\n  tip,\n  update,\n  describe: {\n    title: \"Support ticket topics\",\n    summary: `A sample of ${data.clusters.reduce((a, c) => a + c.n, 0)} support tickets from the last 7 days in ${data.clusters.length} topic clusters: ${data.clusters.map((c) => `${name(c.label)} ${c.n}`).join(\", \")}.`,\n    table: { head: [\"Topic\", \"Tickets shown\"], rows: data.clusters.map((c) => [name(c.label), c.n]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "relationship"
  ],
  "meta": {
    "access": "free",
    "no": 91,
    "family": "Relationship"
  }
}
