{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "query-builder",
  "type": "registry:ui",
  "title": "Query builder",
  "description": "Filter query builder that chains field, operator and value clauses with AND. Add or remove rows, each with labelled selects, built in React and Tailwind.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/button.json",
    "https://ui.minidev.pro/r/icon-button.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/query-builder.tsx",
      "type": "registry:ui",
      "target": "components/ui/query-builder.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 { IconButton } from \"@/components/ui/icon-button\"\n\ntype Clause = { id: string; field: string; op: string; value: string }\n\nconst fieldOpts = [\"status\", \"assignee\", \"priority\"]\nconst opOpts = [\"is\", \"is not\", \"contains\"]\n\nfunction QueryBuilder({ className }: { className?: string }) {\n  const [clauses, setClauses] = React.useState<Clause[]>([\n    { id: \"1\", field: \"status\", op: \"is\", value: \"open\" },\n    { id: \"2\", field: \"assignee\", op: \"is\", value: \"me\" },\n  ])\n  return (\n    <div data-slot=\"query-builder\" className={cn(\"space-y-2 rounded-xl border border-border bg-surface p-3\", className)}>\n      {clauses.map((c, i) => (\n        <div key={c.id} className=\"flex flex-wrap items-center gap-2\">\n          {i > 0 ? <span className=\"w-10 text-xs font-medium text-fg-muted\">AND</span> : <span className=\"w-10 text-xs text-fg-muted\">Where</span>}\n          <label className=\"sr-only\" htmlFor={`field-${c.id}`}>Field</label>\n          <select\n            id={`field-${c.id}`}\n            value={c.field}\n            onChange={(e) => setClauses((prev) => prev.map((x) => x.id === c.id ? { ...x, field: e.target.value } : x))}\n            className=\"h-9 rounded-lg border border-border bg-bg px-2 text-sm text-fg outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n          >\n            {fieldOpts.map((o) => <option key={o} value={o}>{o}</option>)}\n          </select>\n          <label className=\"sr-only\" htmlFor={`op-${c.id}`}>Operator</label>\n          <select\n            id={`op-${c.id}`}\n            value={c.op}\n            onChange={(e) => setClauses((prev) => prev.map((x) => x.id === c.id ? { ...x, op: e.target.value } : x))}\n            className=\"h-9 rounded-lg border border-border bg-bg px-2 text-sm text-fg outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n          >\n            {opOpts.map((o) => <option key={o} value={o}>{o}</option>)}\n          </select>\n          <input\n            aria-label=\"Value\"\n            value={c.value}\n            onChange={(e) => setClauses((prev) => prev.map((x) => x.id === c.id ? { ...x, value: e.target.value } : x))}\n            className=\"h-9 w-28 rounded-lg border border-border bg-bg px-2 text-sm text-fg outline-none focus-visible:ring-2 focus-visible:ring-accent\"\n          />\n          <IconButton type=\"button\" size=\"icon-sm\" variant=\"ghost\" aria-label=\"Remove clause\" onClick={() => setClauses((prev) => prev.filter((x) => x.id !== c.id))}>\n            <TrashIcon />\n          </IconButton>\n        </div>\n      ))}\n      <Button\n        type=\"button\"\n        size=\"sm\"\n        variant=\"outline\"\n        onClick={() => setClauses((prev) => [...prev, { id: String(Date.now()), field: \"status\", op: \"is\", value: \"\" }])}\n      >\n        <PlusIcon /> Add filter\n      </Button>\n    </div>\n  )\n}\nexport { QueryBuilder }\n"
    }
  ]
}
