{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "packed-bubbles",
  "type": "registry:component",
  "title": "Packed bubbles",
  "description": "Items as bubbles, sized by volume. Sample data: spot volume by asset, ETH +5.8%, SOL −3.4%.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-bubbles"
  ],
  "files": [
    {
      "path": "registry/chartory/components/packed-bubbles.tsx",
      "type": "registry:component",
      "target": "components/chartory/packed-bubbles.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Packed bubbles (Chartory chart 035): 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/packed-bubbles\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/035-packed-bubbles/data\"\nimport { def } from \"@/lib/chartory/charts/types/035-packed-bubbles/layout\"\n\nregisterChart(def, sample)\n\nexport type PackedBubblesData = typeof sample\nexport type PackedBubblesProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: PackedBubblesData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: PackedBubblesData[]\n}\n\nexport function PackedBubbles(props: PackedBubblesProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/035-packed-bubbles/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/035-packed-bubbles/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 035 Packed bubbles · sample data (Figma chart/035, crypto: 24 h spot volume in $B per asset and its 24 h change in %).\nimport type { BubblesData } from \"../../plots/bubbles\";\n\nconst items: [string, number, number][] = [\n  [\"BTC\", 28.4, 2.1], [\"ETH\", 14.1, 5.8], [\"SOL\", 4.2, -3.4], [\"XRP\", 2.9, 1.2], [\"DOGE\", 1.8, -6.1], [\"BNB\", 1.6, 0.8],\n  [\"ADA\", 0.9, -2.2], [\"AVAX\", 0.7, 3.9], [\"LINK\", 0.6, -1.4], [\"DOT\", 0.5, 0.6], [\"TON\", 0.45, -0.9], [\"ATOM\", 0.4, 2.4],\n];\n\nexport const data: BubblesData = { rMax: 82, items: items.map(([label, v, chg]) => ({ label, v, chg })) };\n"
    },
    {
      "path": "registry/chartory/charts/types/035-packed-bubbles/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/035-packed-bubbles/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 035 Packed bubbles (Figma chart/035): C.bubbles on the 24 h volume sample.\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { bubbles } from \"../../plots/bubbles\";\nimport { data } from \"./data\";\n\nconst chg = (v: number) => (v >= 0 ? \"+\" : \"−\") + Math.abs(v).toFixed(1) + \"%\";\nconst up = data.items.filter((i) => i.chg >= 0).length;\n\n/** Tooltip for one bubble (group = the asset's rank by volume, as packed): its 24 h volume and change. */\nfunction tipOf(o: typeof data, g: number): TooltipSpec | null {\n  const it = [...o.items].sort((a, b) => b.v - a.v)[g];\n  if (!it) return null;\n  return { kind: \"compare\", caps: `${it.label} · 24 H VOLUME`, value: \"$\" + it.v + \"B\", delta: { text: chg(it.chg), tone: it.chg >= 0 ? \"success\" : \"danger\" }, comparison: \"VS PRIOR 24 H\" };\n}\n\nexport const def: ChartDef = {\n  no: 35,\n  slug: \"packed-bubbles\",\n  legend: [[\"status/success\", \"Gainers\"], [\"status/danger\", \"Losers\"]],\n  // update: the next 24 h; volume moves with the market (±8 %) and a little per asset (order kept), each change moves\n  // ±2.5 points (one decimal); the packing order and the label sizes stay\n  update: (d, step) => {\n    const o = d as typeof data;\n    const k = 1 + 0.08 * Math.sin(step * 2.1);\n    return {\n      ...o,\n      items: o.items.map((it, i) => ({\n        ...it,\n        v: +(it.v * k * (1 + 0.025 * Math.sin(step * 1.9 + i * 1.4))).toFixed(it.v < 1 ? 2 : 1),\n        chg: +(it.chg + 2.5 * Math.sin(step * 1.3 + i * 0.9)).toFixed(1),\n      })),\n    };\n  },\n  plot: (K, p, w, d) => bubbles(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tipOf(d as typeof data, g),\n  describe: {\n    title: \"Volume by asset · 24 h\",\n    summary: `24 h spot volume for ${data.items.length} assets, sized by volume and coloured by change: ${data.items[0].label} leads at $${data.items[0].v}B (${chg(data.items[0].chg)}); ${up} assets gained, ${data.items.length - up} lost.`,\n    table: { head: [\"Asset\", \"Volume\", \"Change\"], rows: data.items.map((i) => [i.label, \"$\" + i.v + \"B\", chg(i.chg)]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "part-of-a-whole"
  ],
  "meta": {
    "access": "free",
    "no": 35,
    "family": "Part of a whole"
  }
}
