{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stat",
  "title": "Stat",
  "description": "Cardless figure with a unit, a caption and a delta.",
  "dependencies": [
    "class-variance-authority@^0.7.1"
  ],
  "registryDependencies": [
    "@pestoui/style",
    "@pestoui/utils"
  ],
  "files": [
    {
      "path": "pestoui/components/stat.tsx",
      "content": "import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* A cardless typographic stat: a figure, an optional muted unit, a caption and\n   a delta. MetricCard is the same trio wearing its own Card; this is the trio\n   alone, so it can sit inside a card, a table cell or a hero without bringing a\n   second surface with it.\n\n   The size lands on the root as a font-size, and the value is the only part\n   that inherits it. Every other part states its own size, weight, family and\n   tracking outright. That is deliberate: a caller's `font-mono` or\n   `font-normal` on the root is how a monospace or regular-weight figure is\n   asked for, and it must reach the value without leaking into the caption\n   beside it.\n\n   No client directive. The only hook is useId, which React's server build exports,\n   and there are no function-valued props — a delta formatter would be a\n   closure created on every render and would drag the file across the client\n   boundary. Callers compose the delta as a node instead. */\nconst statVariants = cva(\"flex min-w-0 flex-col font-semibold tracking-tight\", {\n  variants: {\n    size: {\n      sm: \"gap-1 text-2xl\",\n      default: \"gap-1.5 text-3xl\",\n      lg: \"gap-2 text-4xl\",\n      xl: \"gap-2 text-5xl\",\n    },\n  },\n  defaultVariants: {\n    size: \"default\",\n  },\n})\n\n/* A unit is a qualifier, not a second figure, so it steps well down from the\n   value — about half its size — and drops to muted. It keeps the root's family\n   and weight on purpose: \"/ 5,000\" beside a monospace \"2,380\" should be monospace\n   too. Tracking resets because tight tracking on small text closes the counters\n   up.\n\n   leading-0 keeps the unit out of the line-box arithmetic. At line-height 1 a\n   smaller run on the same baseline still hangs its descent below the value's\n   and grows the row by about 2px, so a stat with a unit would sit taller than\n   its neighbour without one. A wrapped unit still gets a full line, because the\n   figure's own strut sets the height of every line it opens. */\nconst statUnitVariants = cva(\"leading-0 tracking-normal text-muted-foreground\", {\n  variants: {\n    size: {\n      sm: \"text-sm\",\n      default: \"text-base\",\n      lg: \"text-lg\",\n      xl: \"text-xl\",\n    },\n  },\n  defaultVariants: {\n    size: \"default\",\n  },\n})\n\n/* Label and delta share one caption scale. Family, weight and tracking are\n   restated rather than inherited — the root carries the value's typography, and\n   without these resets a monospace or semibold figure turns its own caption\n   monospace and semibold too. */\nconst statCaptionVariants = cva(\n  \"font-sans leading-snug font-normal tracking-normal text-muted-foreground\",\n  {\n    variants: {\n      size: {\n        sm: \"text-xs\",\n        default: \"text-sm\",\n        lg: \"text-sm\",\n        xl: \"text-base\",\n      },\n    },\n    defaultVariants: {\n      size: \"default\",\n    },\n  },\n)\n\ntype StatSize = NonNullable<VariantProps<typeof statVariants>[\"size\"]>\n\ntype StatLabelPosition = \"above\" | \"below\" | \"inline\"\n\ntype StatDeltaPlacement = \"inline\" | \"below\"\n\ntype StatProps = Omit<React.ComponentProps<\"div\">, \"children\"> &\n  VariantProps<typeof statVariants> & {\n    /** The figure. A node, so it can host a NumberTicker or composed parts. */\n    value: React.ReactNode\n    /**\n     * Muted, smaller, on the value's baseline and flush against it — begin the\n     * text with a space (`\" / 1000\"`) when the design has one.\n     */\n    unit?: React.ReactNode\n    /** What the figure measures. When present the root becomes a labelled group. */\n    label?: React.ReactNode\n    /**\n     * `\"above\"` and `\"below\"` stack the label on its own line; `\"inline\"` sets it\n     * on the value's baseline, after the value and any inline delta.\n     */\n    labelPosition?: StatLabelPosition\n    /**\n     * Any node — compose it from `metricDeltaVariants({ trend })` and the glyph\n     * the design uses. The slot itself is muted caption text, so trailing copy\n     * such as \"vs last week\" needs no styling of its own.\n     */\n    delta?: React.ReactNode\n    /** `\"inline\"` sits the delta on the value's row; `\"below\"` gives it its own line. */\n    deltaPlacement?: StatDeltaPlacement\n    /** A node on the value row, before the figure, vertically centred — an icon or a dot. */\n    leading?: React.ReactNode\n  }\n\nfunction Stat({\n  value,\n  unit,\n  label,\n  labelPosition = \"above\",\n  delta,\n  deltaPlacement = \"below\",\n  leading,\n  size = \"default\",\n  className,\n  ...props\n}: StatProps) {\n  const labelId = React.useId()\n  const hasLabel = label != null && label !== false && label !== \"\"\n  const hasDelta = delta != null && delta !== false && delta !== \"\"\n  const caption = statCaptionVariants({ size })\n\n  /* `flex` rather than block so a leading dot or icon passed inside the label\n     centres on the text without the caller writing a wrapper. As a flex item on\n     the value row it still aligns on its first baseline. */\n  const labelNode = hasLabel ? (\n    <span\n      id={labelId}\n      data-slot=\"stat-label\"\n      className={cn(\"flex min-w-0 items-center gap-2\", caption)}\n    >\n      {label}\n    </span>\n  ) : null\n\n  const deltaNode = hasDelta ? (\n    <span\n      data-slot=\"stat-delta\"\n      className={cn(\"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1\", caption)}\n    >\n      {delta}\n    </span>\n  ) : null\n\n  return (\n    <div\n      data-slot=\"stat\"\n      data-size={size}\n      role={hasLabel ? \"group\" : undefined}\n      aria-labelledby={hasLabel ? labelId : undefined}\n      className={cn(statVariants({ size }), className)}\n      {...props}\n    >\n      {labelPosition === \"above\" ? labelNode : null}\n\n      {/* Baseline alignment is what lets an inline caption read as part of the\n          figure rather than floating beside it. It wraps instead of overflowing:\n          a narrow column pushes the inline delta and label onto a second line. */}\n      <div\n        data-slot=\"stat-row\"\n        className=\"flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-1\"\n      >\n        {leading != null ? (\n          <span\n            data-slot=\"stat-leading\"\n            className=\"inline-flex shrink-0 items-center self-center\"\n          >\n            {leading}\n          </span>\n        ) : null}\n\n        {/* An inline formatting context, not a flex row: the value and unit\n            share a baseline for free at two different sizes, and whitespace\n            the caller writes into the unit survives. leading-none sits here\n            and not only on the value, because the line box would otherwise\n            take the root's strut height and pad the figure back out. */}\n        <span data-slot=\"stat-figure\" className=\"min-w-0 leading-none\">\n          <span data-slot=\"stat-value\" className=\"text-foreground tabular-nums\">\n            {value}\n          </span>\n          {unit != null ? (\n            <span data-slot=\"stat-unit\" className={statUnitVariants({ size })}>\n              {unit}\n            </span>\n          ) : null}\n        </span>\n\n        {deltaPlacement === \"inline\" ? deltaNode : null}\n        {labelPosition === \"inline\" ? labelNode : null}\n      </div>\n\n      {labelPosition === \"below\" ? labelNode : null}\n      {deltaPlacement === \"below\" ? deltaNode : null}\n    </div>\n  )\n}\n\nexport { Stat, statVariants }\nexport type { StatProps, StatSize, StatLabelPosition, StatDeltaPlacement }\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}