{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dumbbell",
  "type": "registry:component",
  "title": "Dumbbell",
  "description": "Two values per item, and the gap between them. Sample data: our price against the rival's for six outdoor products, gaps from −$10 to +$7.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-dumbbell"
  ],
  "files": [
    {
      "path": "registry/chartory/components/dumbbell.tsx",
      "type": "registry:component",
      "target": "components/chartory/dumbbell.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Dumbbell (Chartory chart 066): 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/dumbbell\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/066-dumbbell/data\"\nimport { def } from \"@/lib/chartory/charts/types/066-dumbbell/layout\"\n\nregisterChart(def, sample)\n\nexport type DumbbellData = typeof sample\nexport type DumbbellProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: DumbbellData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: DumbbellData[]\n}\n\nexport function Dumbbell(props: DumbbellProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/066-dumbbell/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/066-dumbbell/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 066 Dumbbell · sample data (Figma chart/066, our price (a) against the rival's (b) per product, $).\nimport type { DumbbellData } from \"../../plots/dumbbell\";\n\nexport const data: DumbbellData = {\n  min: 0, max: 200,\n  xt: [[0, \"$0\"], [50, \"$50\"], [100, \"$100\"], [150, \"$150\"], [200, \"$200\"]],\n  rows: [\n    { label: \"Trail runner\", a: 129, b: 139 }, { label: \"Rain shell\", a: 179, b: 172 }, { label: \"Fleece mid\", a: 79, b: 89 },\n    { label: \"Daypack 20L\", a: 64, b: 69 }, { label: \"Wool beanie\", a: 29, b: 24 }, { label: \"Hiking pole\", a: 49, b: 55 },\n  ],\n};\n"
    },
    {
      "path": "registry/chartory/charts/types/066-dumbbell/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/066-dumbbell/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 066 Dumbbell (Figma chart/066, \"Price position\"): C.dumbbell, our price against the rival's on 6 products.\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { dumbbell } from \"../../plots/dumbbell\";\nimport { data } from \"./data\";\n\nconst gap = (a: number, b: number) => (a - b > 0 ? \"+$\" : \"−$\") + Math.abs(a - b);\nconst cheaper = data.rows.filter((r) => r.a <= r.b).length;\n\n// tooltip: one product row (group = row): our price against the rival's, tone = the gap bar's colour\nconst tip = (o: typeof data, g: number): TooltipSpec | null => {\n  const r = o.rows[g];\n  if (!r) return null;\n  return { kind: \"compare\", caps: r.label.toUpperCase() + \" · OUR PRICE\", value: \"$\" + r.a, delta: { text: gap(r.a, r.b), tone: r.a <= r.b ? \"success\" : \"danger\" }, comparison: \"VS RIVAL $\" + r.b };\n};\n\nexport const def: ChartDef = {\n  no: 66,\n  slug: \"dumbbell\",\n  legend: [[\"data/primary\", \"Our price\"], [\"data/cat-2\", \"Rival price\"]],\n  plot: (K, p, w, d) => dumbbell(K, p, w, (d as typeof data | undefined) ?? data),\n  tip: (d, g) => tip(d as typeof data, g),\n  // the next price check: both prices move a few dollars (never equal, inside the axis); the gaps are recomputed by the plot\n  update: (d, step) => {\n    const o = d as typeof data;\n    return {\n      ...o,\n      rows: o.rows.map((r, k) => {\n        const a = Math.min(o.max, Math.max(o.min + 1, r.a + Math.round(4 * Math.sin(k * 1.9 + step * 2.3))));\n        const b = Math.min(o.max, Math.max(o.min + 1, r.b + Math.round(5 * Math.sin(k * 1.1 + step * 1.7 + 1))));\n        return { ...r, a: a === b ? a - 1 : a, b };\n      }),\n    };\n  },\n  describe: {\n    title: \"Price watch\",\n    summary: `Our price is lower than the rival's on ${cheaper} of ${data.rows.length} products; the widest gaps are −$10 (Trail runner, Fleece mid) and +$7 (Rain shell).`,\n    table: { head: [\"Product\", \"Our price\", \"Rival price\", \"Gap\"], rows: data.rows.map((r) => [r.label, \"$\" + r.a, \"$\" + r.b, gap(r.a, r.b)]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "compare-items"
  ],
  "meta": {
    "access": "free",
    "no": 66,
    "family": "Compare items"
  }
}
