{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "plug-picker",
  "title": "Plug picker",
  "description": "A grid of selectable plugs — the picker body for swapping a mod, perk, aspect, or fragment.",
  "dependencies": [
    "@engram/core"
  ],
  "registryDependencies": [
    "@engram/cn",
    "@engram/item-grid",
    "@engram/mod-slot",
    "@engram/plug",
    "@engram/tokens"
  ],
  "files": [
    {
      "path": "src/components/plug-picker.tsx",
      "content": "import type { PlugProps } from \"@engram/core\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/cn.js\";\nimport { ItemGrid } from \"./item-grid.js\";\nimport { ModSlot } from \"./mod-slot.js\";\nimport { Plug } from \"./plug.js\";\n\nexport interface PlugPickerOption extends PlugProps {\n  /** Section this option falls under when `groupBy` is used. */\n  group?: string;\n  /** Energy cost — shown as a corner pip on mod options. */\n  energyCost?: number;\n}\n\nexport interface PlugPickerProps\n  extends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n  options: PlugPickerOption[];\n  /** Hash of the currently selected plug. */\n  selected?: number;\n  onSelect?: (option: PlugPickerOption) => void;\n  /** Group options under headers (defaults to each option's `group`). */\n  groupBy?: (option: PlugPickerOption) => string | undefined;\n  /** Perks render as circles, mods as squares. */\n  kind?: \"perk\" | \"mod\";\n  /** Pixel size per option. */\n  size?: number;\n}\n\n/**\n * A grid of selectable plugs — the picker body for swapping a mod, perk, aspect,\n * or fragment. Optionally sections options under group headers. Drop it into a\n * {@link Sheet} for the in-game \"choose a plug\" flyout; the consumer owns the\n * option list and the selection state.\n */\nexport function PlugPicker({\n  options,\n  selected,\n  onSelect,\n  groupBy,\n  kind = \"mod\",\n  size = 48,\n  className,\n  ...props\n}: PlugPickerProps) {\n  const groups = new Map<string, PlugPickerOption[]>();\n  for (const opt of options) {\n    const key = (groupBy ? groupBy(opt) : opt.group) ?? \"\";\n    const list = groups.get(key);\n    if (list) list.push(opt);\n    else groups.set(key, [opt]);\n  }\n\n  const renderOption = (opt: PlugPickerOption) =>\n    kind === \"mod\" ? (\n      <ModSlot\n        key={opt.hash}\n        plug={opt}\n        energyCost={opt.energyCost}\n        selected={opt.hash === selected}\n        size={size}\n        onPick={onSelect ? () => onSelect(opt) : undefined}\n      />\n    ) : (\n      <Plug\n        key={opt.hash}\n        {...opt}\n        kind={kind}\n        selected={opt.hash === selected}\n        size={size}\n        onClick={onSelect ? () => onSelect(opt) : undefined}\n      />\n    );\n\n  return (\n    <div className={cn(\"flex flex-col gap-3\", className)} {...props}>\n      {[...groups].map(([group, opts]) => (\n        <div key={group || \"all\"} className=\"flex flex-col gap-1.5\">\n          {group ? (\n            <span className=\"font-engram-display text-[10px] text-engram-faint uppercase tracking-engram-label\">\n              {group}\n            </span>\n          ) : null}\n          <ItemGrid min={size} gap={6}>\n            {opts.map(renderOption)}\n          </ItemGrid>\n        </div>\n      ))}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/engram/plug-picker.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": [
    "socket"
  ],
  "type": "registry:component"
}