{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hex-grid",
  "type": "registry:component",
  "title": "Hex grid",
  "description": "Where clicks pile up on a page. Sample data: clicks on three key pages, product 16.1K, home 14.2K, checkout 8.3K.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-hexbin"
  ],
  "files": [
    {
      "path": "registry/chartory/components/hex-grid.tsx",
      "type": "registry:component",
      "target": "components/chartory/hex-grid.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Hex grid (Chartory chart 028): 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/hex-grid\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/028-hex-grid/data\"\nimport { def } from \"@/lib/chartory/charts/types/028-hex-grid/layout\"\n\nregisterChart(def, sample)\n\nexport type HexGridData = typeof sample\nexport type HexGridProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: HexGridData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: HexGridData[]\n}\n\nexport function HexGrid(props: HexGridProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/028-hex-grid/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/028-hex-grid/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 028 Hex grid · sample data (Figma chart/028, e-commerce: this week's click density on 3 key pages; each blob is\n// [x, y, spread, weight] in the page's unit space).\nimport type { HexbinData } from \"../../plots/hexbin\";\n\nexport const data: HexbinData = {\n  panels: [\n    { name: \"Home\", sub: \"14.2K CLICKS\", blobs: [[0.5, 0.22, 0.18, 1], [0.25, 0.72, 0.15, 0.6]] },\n    { name: \"Product\", sub: \"16.1K CLICKS\", blobs: [[0.72, 0.55, 0.15, 1], [0.3, 0.3, 0.2, 0.7]] },\n    { name: \"Checkout\", sub: \"8.3K CLICKS\", blobs: [[0.5, 0.86, 0.14, 1], [0.5, 0.4, 0.18, 0.5]] },\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/028-hex-grid/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/028-hex-grid/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 028 Hex grid (Figma chart/028): C.hexbin; no legend (panels are named).\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { hexbin } from \"../../plots/hexbin\";\nimport { data } from \"./data\";\n\nconst area = (y: number) => (y < 0.34 ? \"top\" : y < 0.67 ? \"middle\" : \"bottom\");\nconst hot = (b: [number, number, number, number][]) => [...b].sort((a, c) => c[3] - a[3])[0];\n\n/** Tooltip for one page panel (group = the panel): its clicks, their share of all panels and where they are densest. */\nfunction tipOf(o: typeof data, g: number): TooltipSpec | null {\n  const p = o.panels[g];\n  if (!p) return null;\n  const all = o.panels.reduce((s, q) => s + parseFloat(q.sub), 0), v = parseFloat(p.sub);\n  return { kind: \"cell\", caps: `${p.name.toUpperCase()} · DENSEST AT ${area(hot(p.blobs)[1]).toUpperCase()}`, value: p.sub.split(\" \")[0], unit: \"clicks\", swatch: \"data/primary\", share: Math.round((v / all) * 100) + \"% OF ALL CLICKS\" };\n}\n\nexport const def: ChartDef = {\n  no: 28,\n  slug: \"hex-grid\",\n  legend: null,\n  // update: next week's clicks; each page's count moves 5–15 %. The density blobs stay: any change to them moves the\n  // 0.12 cut-off and adds or drops edge bins (no mark-by-mark morph), so the heat pattern reads as the same shape\n  update: (d, step) => {\n    const o = d as typeof data;\n    return {\n      ...o,\n      panels: o.panels.map((p, q) => {\n        const k = 1 + 0.1 * Math.sin(step * 1.7 + q * 2.1) + 0.04 * Math.sign(Math.sin(step * 1.7 + q * 2.1));\n        const clicks = parseFloat(p.sub) * k;\n        return { ...p, sub: clicks.toFixed(1) + \"K CLICKS\" };\n      }),\n    };\n  },\n  plot: (K, p, w, d) => hexbin(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tipOf(d as typeof data, g),\n  describe: {\n    title: \"Click density by page\",\n    summary: `Click density this week on ${data.panels.length} pages: ${data.panels.map((p) => `${p.name} ${p.sub.split(\" \")[0]}`).join(\", \")} clicks; ${data.panels.map((p) => `${p.name} is densest in the ${area(hot(p.blobs)[1])}`).join(\", \")}.`,\n    table: { head: [\"Page\", \"Clicks\", \"Densest area\"], rows: data.panels.map((p) => [p.name, p.sub.split(\" \")[0], area(hot(p.blobs)[1])]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "space-and-place"
  ],
  "meta": {
    "access": "free",
    "no": 28,
    "family": "Space and place"
  }
}
