{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mod-tray",
  "title": "Mod tray",
  "description": "A row of ModSlots with an energy-budget meter — the armor mod loadout for one piece. `used` defaults to the summed energy costs; the meter turns over-budget red when the slots exceed `capacity`.",
  "dependencies": [
    "@engram/core"
  ],
  "registryDependencies": [
    "@engram/cn",
    "@engram/energy-meter",
    "@engram/mod-slot",
    "@engram/tokens"
  ],
  "files": [
    {
      "path": "src/components/mod-tray.tsx",
      "content": "import type { PlugProps } from \"@engram/core\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { EnergyMeter } from \"./energy-meter.js\";\nimport { ModSlot } from \"./mod-slot.js\";\n\nexport interface ModTraySlot {\n  plug?: PlugProps;\n  energyCost?: number;\n}\n\nexport interface ModTrayProps extends HTMLAttributes<HTMLDivElement> {\n  /** The mod sockets in this tray. */\n  slots: ModTraySlot[];\n  /** Total energy capacity of the armor piece. */\n  capacity: number;\n  /** Energy spent; defaults to the sum of the slots' energy costs. */\n  used?: number;\n  /** Pixel size per socket. */\n  size?: number;\n  /** Fired with the socket index when a socket is clicked. */\n  onPick?: (index: number) => void;\n  /** Inspect content for a filled socket (e.g. a {@link ModPopup}), shown on\n   *  hover — wires each socket to an {@link ItemHoverCard}. */\n  popup?: (plug: PlugProps, index: number) => ReactNode;\n}\n\n/**\n * A row of {@link ModSlot}s with an energy-budget meter — the armor mod loadout\n * for one piece. `used` defaults to the summed energy costs; the meter turns\n * over-budget red when the slots exceed `capacity`.\n */\nexport function ModTray({\n  slots,\n  capacity,\n  used,\n  size = 44,\n  onPick,\n  popup,\n  className,\n  ...props\n}: ModTrayProps) {\n  const spent = used ?? slots.reduce((sum, s) => sum + (s.energyCost ?? 0), 0);\n  const over = spent > capacity;\n  return (\n    <div className={cn(\"flex flex-col gap-2\", className)} {...props}>\n      <div className=\"flex flex-wrap gap-1.5\">\n        {slots.map((slot, i) => (\n          <ModSlot\n            // positional mod sockets — index is the stable identity\n            // biome-ignore lint/suspicious/noArrayIndexKey: positional sockets, index is the identity\n            key={i}\n            plug={slot.plug}\n            energyCost={slot.energyCost}\n            size={size}\n            onPick={onPick ? () => onPick(i) : undefined}\n            popup={slot.plug && popup ? popup(slot.plug, i) : undefined}\n          />\n        ))}\n      </div>\n      <div className=\"flex items-center gap-2\">\n        <EnergyMeter\n          used={Math.min(spent, capacity)}\n          total={capacity}\n          color={over ? \"var(--engram-bad)\" : \"var(--engram-accent)\"}\n        />\n        <span\n          className={cn(\n            \"font-engram-display text-[11px] tabular-nums\",\n            over ? \"text-engram-bad\" : \"text-engram-faint\",\n          )}\n        >\n          {spent} / {capacity}\n        </span>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/engram/mod-tray.tsx"
    }
  ],
  "meta": {
    "level": "component"
  },
  "docs": "Extend without forking: edit the copied source, use `asChild` (Radix Slot) to change the rendered element, pass the typed `annotations` prop for curated data (verdict/tags/per-plug), or use slot / render-prop props for arbitrary content. Requires the @engram/tokens theme (--engram-* CSS variables).",
  "categories": [
    "loadout"
  ],
  "type": "registry:component"
}