{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "multi-select",
  "type": "registry:ui",
  "title": "Multi select",
  "description": "A multi select dropdown in a popover with check marks on chosen items and removable chips below the trigger. Works controlled or uncontrolled in React.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://ui.minidev.pro/r/popover.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/multi-select.tsx",
      "type": "registry:ui",
      "target": "components/ui/multi-select.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { CheckIcon, ChevronsUpDownIcon } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\"\n\n/** MultiSelect — a popover list with check marks and removable chips for the chosen items. */\n\ntype Item = { value: string; label: string }\n\n// Reviewers who can approve an invoice in Lumen.\nconst SAMPLE_ITEMS: Item[] = [\n  { value: \"maya-chen\", label: \"Maya Chen\" },\n  { value: \"theo-okafor\", label: \"Theo Okafor\" },\n  { value: \"priya-raman\", label: \"Priya Raman\" },\n  { value: \"sam-lowe\", label: \"Sam Lowe\" },\n  { value: \"ada-novak\", label: \"Ada Novak\" },\n  { value: \"jonas-weber\", label: \"Jonas Weber\" },\n]\n// Preselected only for the built-in demo data.\nconst SAMPLE_SELECTED = [\"maya-chen\", \"priya-raman\"]\n\ntype MultiSelectProps = {\n  items?: Item[]\n  /** Controlled selection. */\n  value?: string[]\n  /** Initial selection when uncontrolled. */\n  defaultValue?: string[]\n  onChange?: (value: string[]) => void\n  placeholder?: string\n  disabled?: boolean\n  className?: string\n}\n\nfunction MultiSelect({ items = SAMPLE_ITEMS, value, defaultValue, onChange, placeholder = \"Select…\", disabled, className }: MultiSelectProps) {\n  const [open, setOpen] = React.useState(false)\n  const [internal, setInternal] = React.useState<string[]>(() => defaultValue ?? (items === SAMPLE_ITEMS ? SAMPLE_SELECTED : []))\n  const selected = value ?? internal\n  const setSelected = (next: string[]) => {\n    if (value === undefined) setInternal(next)\n    onChange?.(next)\n  }\n  const labels = items.filter((i) => selected.includes(i.value))\n  return (\n    <div data-slot=\"multi-select\" className=\"space-y-2\">\n      <Popover open={open} onOpenChange={setOpen}>\n        <PopoverTrigger\n          disabled={disabled}\n          aria-label={placeholder}\n          className={cn(\n            \"flex min-h-9 w-72 items-center justify-between gap-2 rounded-lg border border-border bg-surface px-3 py-1.5 text-left text-sm\",\n            \"shadow-highlight outline-none hover:border-fg-subtle\",\n            \"focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-bg\",\n            \"disabled:opacity-50\",\n            className\n          )}\n        >\n          <span className={cn(\"truncate\", labels.length === 0 && \"text-fg-subtle\")}>\n            {labels.length === 0 ? placeholder : labels.map((l) => l.label).join(\", \")}\n          </span>\n          <ChevronsUpDownIcon className=\"size-4 shrink-0 text-fg-muted\" />\n        </PopoverTrigger>\n        <PopoverContent align=\"start\" className=\"w-72 p-1\">\n          {items.map((item) => {\n            const on = selected.includes(item.value)\n            return (\n              <button\n                key={item.value}\n                type=\"button\"\n                className=\"flex w-full items-center gap-2 rounded-lg px-2.5 py-1.5 text-left text-sm hover:bg-sunken\"\n                onClick={() => setSelected(on ? selected.filter((v) => v !== item.value) : [...selected, item.value])}\n              >\n                <CheckIcon className={cn(\"size-4\", on ? \"opacity-100 text-accent\" : \"opacity-0\")} />\n                {item.label}\n              </button>\n            )\n          })}\n        </PopoverContent>\n      </Popover>\n      {labels.length > 0 ? (\n        <div className=\"flex flex-wrap gap-1.5\">\n          {labels.map((l) => (\n            <button\n              key={l.value}\n              type=\"button\"\n              className=\"inline-flex h-6 items-center gap-1 rounded-md border border-border bg-sunken px-2 text-xs text-fg\"\n              onClick={() => setSelected(selected.filter((v) => v !== l.value))}\n            >\n              {l.label}\n              <span aria-hidden className=\"text-fg-muted\">×</span>\n            </button>\n          ))}\n        </div>\n      ) : null}\n    </div>\n  )\n}\nexport { MultiSelect }\nexport type { MultiSelectProps, Item as MultiSelectItem }\n"
    }
  ]
}
