{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "bullet",
  "type": "registry:component",
  "title": "Bullet",
  "description": "Each number against its target. Sample data: four quarter-to-date measures, revenue $428K of $500K, orders 6,120 of 7,000.",
  "registryDependencies": [
    "@chartory/core",
    "@chartory/plot-bullet"
  ],
  "files": [
    {
      "path": "registry/chartory/components/bullet.tsx",
      "type": "registry:component",
      "target": "components/chartory/bullet.tsx",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// Bullet (Chartory chart 008): 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/bullet\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/008-bullet/data\"\nimport { def } from \"@/lib/chartory/charts/types/008-bullet/layout\"\n\nregisterChart(def, sample)\n\nexport type BulletData = typeof sample\nexport type BulletProps = Omit<ChartViewProps, \"def\" | \"data\" | \"states\"> & {\n  /** the data to draw (default: the sample) */\n  data?: BulletData\n  /** further data states: the chart morphs to them (update) or ticks through them (live, with interval) */\n  states?: BulletData[]\n}\n\nexport function Bullet(props: BulletProps) {\n  return <ChartView def={def} {...props} />\n}\n"
    },
    {
      "path": "registry/chartory/charts/types/008-bullet/data.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/008-bullet/data.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 008 Bullet · sample data (Figma chart/008-bullet, e-commerce: Q3 goals, actual against target with three ranges).\n// The label column is 112 px on L (plot wider than 480), 96 px below.\nimport type { BulletData, BulletRow } from \"../../plots/bullet\";\n\nexport const rows: BulletRow[] = [\n  { label: \"Revenue\", sub: \"USD\", v: 428, target: 500, max: 600, bands: [300, 450, 600], text: \"$428K\", ttext: \"OF $500K\" },\n  { label: \"Orders\", sub: \"COUNT\", v: 6120, target: 7000, max: 8000, bands: [5000, 6500, 8000], text: \"6,120\", ttext: \"OF 7,000\" },\n  { label: \"Avg order\", sub: \"USD\", v: 70, target: 68, max: 90, bands: [55, 70, 90], text: \"$70\", ttext: \"OF $68\" },\n  { label: \"Repeat buyers\", sub: \"SHARE\", v: 31, target: 35, max: 50, bands: [20, 30, 50], text: \"31%\", ttext: \"OF 35%\" },\n];\nexport const data = (w: number): BulletData => ({ labelW: w > 480 ? 112 : 96, rows });\n"
    },
    {
      "path": "registry/chartory/charts/types/008-bullet/layout.ts",
      "type": "registry:lib",
      "target": "lib/chartory/charts/types/008-bullet/layout.ts",
      "content": "// SPDX-License-Identifier: MIT · Copyright (c) 2026 BEY AGENCY LTD · Chartory, usechartory.com\n// 008 Bullet (Figma chart/008-bullet): C.bullet on the Q3 goals sample.\nimport type { ChartDef, TooltipSpec } from \"../../core/shell\";\nimport { bullet } from \"../../plots/bullet\";\nimport { data, rows } from \"./data\";\n\n/** Deterministic wave in [-1, 1] per index and update step (no randomness: the same step gives the same period). */\nconst wave = (i: number, step: number, f = 1.7) => Math.sin(step * 2.3 + i * f + 0.5);\ntype Data = ReturnType<typeof data>;\n/** The sample is a function of the plot width (label column 112 / 96); update states are too. */\nconst at = (d: unknown, w: number): Data => (typeof d === \"function\" ? (d as typeof data)(w) : (d as Data | undefined) ?? data(w));\n/** Each goal's own value format, as in the sample ($428K, 6,120, $70, 31%). */\nconst FMT: Record<string, (v: number) => string> = {\n  Revenue: (v) => \"$\" + v + \"K\",\n  Orders: (v) => v.toLocaleString(\"en-US\"),\n  \"Avg order\": (v) => \"$\" + v,\n  \"Repeat buyers\": (v) => v + \"%\",\n};\n\nconst of = (t: string) => t.replace(/^OF /, \"\");\n\n/** Hover tooltip: one goal's actual against its target (the gap in the goal's own format; share goals in points). */\nconst tip = (d: unknown, g: number): TooltipSpec => {\n  const r = at(d, 440).rows[g], gap = r.v - r.target;\n  const diff = r.sub === \"SHARE\" ? Math.abs(gap) + (Math.abs(gap) === 1 ? \" pt\" : \" pts\") : FMT[r.label](Math.abs(gap));\n  return {\n    kind: \"compare\",\n    caps: r.label.toUpperCase() + \" · Q3\",\n    value: r.text,\n    delta: { text: (gap < 0 ? \"−\" : \"+\") + diff, tone: gap >= 0 ? \"success\" : r.v < r.bands[0] ? \"danger\" : \"warning\" },\n    comparison: \"VS TARGET \" + of(r.ttext),\n  };\n};\n\nexport const def: ChartDef = {\n  no: 8,\n  slug: \"bullet\",\n  legend: [[\"data/primary\", \"Actual\"], [\"text/strong\", \"Target\"], [\"data/track\", \"Ranges\"]],\n  plot: (K, p, w, d) => bullet(K, p, w, at(d, w)),\n  tip,\n  // later weeks of the quarter: each actual moves 4 % a step with a ±6 % swing, inside its range; targets hold\n  update: (d, step) => (w: number): Data => {\n    const o = at(d, w);\n    return {\n      ...o,\n      rows: o.rows.map((r, i) => {\n        const v = Math.min(Math.round(r.max * 0.98), Math.max(1, Math.round(r.v * (1 + 0.04 * step + 0.06 * wave(i, step)))));\n        return { ...r, v, text: FMT[r.label](v) };\n      }),\n    };\n  },\n  describe: {\n    title: \"Q3 goals\",\n    summary: `Q3 goals, actual against target: ${rows.map((r) => `${r.label.toLowerCase()} ${r.text} of ${of(r.ttext)}`).join(\", \")}.`,\n    table: { head: [\"Goal\", \"Unit\", \"Actual\", \"Target\"], rows: rows.map((r) => [r.label, r.sub.charAt(0) + r.sub.slice(1).toLowerCase(), r.text, of(r.ttext)]) },\n  },\n};\n"
    }
  ],
  "categories": [
    "charts",
    "compare-items"
  ],
  "meta": {
    "access": "free",
    "no": 8,
    "family": "Compare items"
  }
}
