{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mod-slot",
  "title": "Mod slot",
  "description": "One armor mod socket: a square plug with its energy-cost pip in the corner, or an empty \"+\" placeholder.",
  "dependencies": [
    "@engram/core"
  ],
  "registryDependencies": [
    "@engram/bungie-image",
    "@engram/cn",
    "@engram/item-hover-card",
    "@engram/tokens"
  ],
  "files": [
    {
      "path": "src/components/mod-slot.tsx",
      "content": "import type { PlugProps } from \"@engram/core\";\nimport type { ButtonHTMLAttributes, ReactNode } from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { BungieImage } from \"./bungie-image.js\";\nimport { ItemHoverCard } from \"./item-hover-card.js\";\n\nexport interface ModSlotProps\n  extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"type\" | \"name\"> {\n  /** The inserted armor mod; omit for an empty socket. */\n  plug?: PlugProps;\n  /** Energy cost — shown as a small pip in the corner. */\n  energyCost?: number;\n  /** Force the empty placeholder. */\n  empty?: boolean;\n  /** Fired when the socket is clicked (open the plug picker). */\n  onPick?: () => void;\n  /** Inspect content shown on hover (e.g. a {@link ModPopup}). When set, the\n   *  socket becomes the trigger of an {@link ItemHoverCard}. */\n  popup?: ReactNode;\n  /** Pixel size. */\n  size?: number;\n  selected?: boolean;\n}\n\n/**\n * One armor mod socket: a square plug with its energy-cost pip in the corner, or\n * an empty \"+\" placeholder. The atom of a {@link ModTray}; clickable to pick.\n */\nexport function ModSlot({\n  plug,\n  energyCost,\n  empty = false,\n  onPick,\n  popup,\n  size = 44,\n  selected = false,\n  className,\n  ...props\n}: ModSlotProps) {\n  const isEmpty = empty || !plug;\n  const socket = (\n    <span\n      className=\"relative inline-block shrink-0\"\n      style={{ width: size, height: size }}\n    >\n      <button\n        type=\"button\"\n        onClick={onPick}\n        title={plug?.name}\n        aria-label={plug?.name ?? \"Empty mod slot\"}\n        aria-pressed={selected}\n        className={cn(\n          \"grid h-full w-full place-items-center overflow-hidden border transition-colors\",\n          selected\n            ? \"border-engram-fg bg-engram-raised-2 shadow-[0_0_8px_rgba(255,255,255,0.3)]\"\n            : \"border-engram-border-strong bg-engram-raised hover:border-engram-fg/60\",\n          onPick && \"cursor-pointer\",\n          className,\n        )}\n        {...props}\n      >\n        {!isEmpty && plug?.icon ? (\n          <BungieImage\n            src={plug.icon}\n            alt=\"\"\n            aria-hidden\n            className=\"h-full w-full scale-[1.18] object-contain\"\n          />\n        ) : (\n          <span\n            aria-hidden\n            className=\"text-engram-faint\"\n            style={{ fontSize: Math.round(size * 0.4), lineHeight: 1 }}\n          >\n            +\n          </span>\n        )}\n      </button>\n      {energyCost != null ? (\n        <span\n          className=\"pointer-events-none absolute right-0 bottom-0 flex min-w-[14px] items-center justify-center bg-engram-bg px-0.5 font-engram-display font-bold text-[10px] text-engram-gold tabular-nums leading-tight\"\n          aria-hidden\n        >\n          {energyCost}\n        </span>\n      ) : null}\n    </span>\n  );\n\n  // The socket is itself a <button>, so the hover wrapper renders a <span> to\n  // avoid nesting interactive elements; the socket's own focus/click bubble up.\n  if (popup && !isEmpty)\n    return (\n      <ItemHoverCard as=\"span\" popup={popup}>\n        {socket}\n      </ItemHoverCard>\n    );\n  return socket;\n}\n",
      "type": "registry:component",
      "target": "components/engram/mod-slot.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"
}