{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "transfer-list",
  "title": "Transfer List",
  "description": "Dual-list picker to move items between available and selected panels.",
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "components/ui/transfer-list.tsx",
      "content": "'use client'\n\nimport * as React from 'react'\n\nimport { Button } from '@/components/ui/button'\n\ntype TransferItem = {\n  id: string\n  label: string\n}\n\ntype TransferListProps = {\n  available: TransferItem[]\n  selected: TransferItem[]\n  onChange: (next: { available: TransferItem[]; selected: TransferItem[] }) => void\n}\n\nfunction pick(list: TransferItem[], ids: Set<string>) {\n  return list.filter((item) => ids.has(item.id))\n}\n\nfunction omit(list: TransferItem[], ids: Set<string>) {\n  return list.filter((item) => !ids.has(item.id))\n}\n\nfunction Panel({\n  title,\n  items,\n  active,\n  toggle,\n}: {\n  title: string\n  items: TransferItem[]\n  active: Set<string>\n  toggle: (id: string) => void\n}) {\n  return (\n    <div className=\"flex-1 rounded border\">\n      <div className=\"border-b px-3 py-2 text-sm font-medium\">{title}</div>\n      <div className=\"max-h-60 space-y-1 overflow-y-auto p-2\">\n        {items.map((item) => (\n          <label key={item.id} className=\"flex cursor-pointer items-center gap-2 rounded px-2 py-1 hover:bg-muted\">\n            <input\n              type=\"checkbox\"\n              checked={active.has(item.id)}\n              onChange={() => toggle(item.id)}\n            />\n            <span className=\"text-sm\">{item.label}</span>\n          </label>\n        ))}\n      </div>\n    </div>\n  )\n}\n\nexport function TransferList({ available, selected, onChange }: TransferListProps) {\n  const [activeAvailable, setActiveAvailable] = React.useState<Set<string>>(new Set())\n  const [activeSelected, setActiveSelected] = React.useState<Set<string>>(new Set())\n\n  const moveRight = () => {\n    const moveItems = pick(available, activeAvailable)\n    onChange({\n      available: omit(available, activeAvailable),\n      selected: [...selected, ...moveItems],\n    })\n    setActiveAvailable(new Set())\n  }\n\n  const moveLeft = () => {\n    const moveItems = pick(selected, activeSelected)\n    onChange({\n      available: [...available, ...moveItems],\n      selected: omit(selected, activeSelected),\n    })\n    setActiveSelected(new Set())\n  }\n\n  return (\n    <div className=\"flex flex-col gap-3 md:flex-row md:items-center\">\n      <Panel\n        title=\"Available\"\n        items={available}\n        active={activeAvailable}\n        toggle={(id) => {\n          setActiveAvailable((prev) => {\n            const next = new Set(prev)\n            if (next.has(id)) next.delete(id)\n            else next.add(id)\n            return next\n          })\n        }}\n      />\n      <div className=\"flex flex-row gap-2 self-center md:flex-col\">\n        <Button type=\"button\" onClick={moveRight} disabled={activeAvailable.size === 0}>\n          Add\n        </Button>\n        <Button type=\"button\" variant=\"outline\" onClick={moveLeft} disabled={activeSelected.size === 0}>\n          Remove\n        </Button>\n      </div>\n      <Panel\n        title=\"Selected\"\n        items={selected}\n        active={activeSelected}\n        toggle={(id) => {\n          setActiveSelected((prev) => {\n            const next = new Set(prev)\n            if (next.has(id)) next.delete(id)\n            else next.add(id)\n            return next\n          })\n        }}\n      />\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "input",
    "picker"
  ],
  "type": "registry:ui"
}