{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ip-allowlist",
  "type": "registry:ui",
  "title": "Ip allowlist",
  "description": "An editor for IP and CIDR allowlists: a monospace list with a remove button per entry plus an input and Add button that skips duplicates. Built in React.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/button.json",
    "https://ui.minidev.pro/r/icon-button.json",
    "https://ui.minidev.pro/r/input.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/ip-allowlist.tsx",
      "type": "registry:ui",
      "target": "components/ui/ip-allowlist.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { PlusIcon, TrashIcon } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { inputVariants } from \"@/components/ui/input\"\nimport { IconButton } from \"@/components/ui/icon-button\"\n\nfunction IpAllowlist({\n  value,\n  onChange,\n  className,\n}: {\n  value?: string[]\n  onChange?: (ips: string[]) => void\n  className?: string\n}) {\n  const [internal, setInternal] = React.useState<string[]>([\"203.0.113.10\"])\n  const ips = value ?? internal\n  const [draft, setDraft] = React.useState(\"\")\n  const setIps = (next: string[]) => {\n    if (value === undefined) setInternal(next)\n    onChange?.(next)\n  }\n  return (\n    <div data-slot=\"ip-allowlist\" className={cn(\"space-y-3\", className)}>\n      <ul className=\"space-y-2\">\n        {ips.map((ip) => (\n          <li key={ip} className=\"flex items-center gap-2 rounded-lg border border-border bg-surface px-3 py-2\">\n            <span className=\"flex-1 font-mono text-sm text-fg\">{ip}</span>\n            <IconButton type=\"button\" size=\"icon-sm\" variant=\"ghost\" aria-label={`Remove ${ip}`} onClick={() => setIps(ips.filter((x) => x !== ip))}>\n              <TrashIcon />\n            </IconButton>\n          </li>\n        ))}\n      </ul>\n      <div className=\"flex gap-2\">\n        <input\n          aria-label=\"IP address\"\n          placeholder=\"203.0.113.0/24\"\n          value={draft}\n          onChange={(e) => setDraft(e.target.value)}\n          className={cn(inputVariants({ size: \"default\" }), \"flex-1 font-mono\")}\n        />\n        <Button\n          type=\"button\"\n          variant=\"outline\"\n          onClick={() => {\n            const v = draft.trim()\n            if (!v || ips.includes(v)) return\n            setIps([...ips, v])\n            setDraft(\"\")\n          }}\n        >\n          <PlusIcon /> Add\n        </Button>\n      </div>\n    </div>\n  )\n}\nexport { IpAllowlist }\n"
    }
  ]
}
