{
  "name": "MultiSelect",
  "package": "@magicblocksai/ui",
  "file": "packages/ui/src/components/MultiSelect.tsx",
  "chapterTag": "04 Forms & Inputs",
  "chapter": "05-forms-and-inputs.html",
  "sectionId": "multi-select",
  "elName": "MultiSelect",
  "demoUrl": "https://brand.magicblocks.ai/components/05-forms-and-inputs#multi-select",
  "hasLiveDemo": false,
  "description": "Multi-select with chip-rendered selections and a keyboard-navigable\npopover. Composes Combobox-style search with persistent multi-pick (no\nauto-close on select). Backspace at the empty input removes the last\nchip.",
  "useClient": true,
  "interactivity": "interactive",
  "namedExports": [
    {
      "name": "MultiSelect",
      "isPrincipal": true,
      "isType": false
    },
    {
      "name": "MultiSelectProps",
      "isPrincipal": false,
      "isType": true
    },
    {
      "name": "MultiSelectOption",
      "isPrincipal": false,
      "isType": false
    }
  ],
  "importStatement": "import { MultiSelect } from \"@magicblocksai/ui\";",
  "props": [
    {
      "name": "value",
      "optional": false,
      "type": "string[]",
      "doc": "Currently selected values."
    },
    {
      "name": "onChange",
      "optional": false,
      "type": "(next: string[]) => void",
      "doc": "Fires whenever the selection set changes."
    },
    {
      "name": "options",
      "optional": false,
      "type": "MultiSelectOption[]",
      "doc": "All options. The component filters in-memory by `label` substring when `searchable` (default `true`). For server-side filtering, recompute `options` in response to `onSearchChange`."
    },
    {
      "name": "placeholder",
      "optional": true,
      "type": "string",
      "doc": "Placeholder shown when no values are selected."
    },
    {
      "name": "disabled",
      "optional": true,
      "type": "boolean",
      "doc": "Disable input + popover."
    },
    {
      "name": "maxChips",
      "optional": true,
      "type": "number",
      "doc": "When the chip count exceeds this, collapse the overflow into a `+N more` chip so the input doesn't grow unbounded. Default: no cap."
    },
    {
      "name": "searchable",
      "optional": true,
      "type": "boolean",
      "doc": "Allow typing to filter the visible options. Default `true`."
    },
    {
      "name": "onSearchChange",
      "optional": true,
      "type": "(query: string) => void",
      "doc": "Fires when the typed query changes — useful for server-side option resolution. The kit doesn't debounce; do it on your side if needed."
    },
    {
      "name": "renderChip",
      "optional": true,
      "type": "( value: string, option: MultiSelectOption | undefined, onRemove: () => void, ) => ReactNode",
      "doc": "Render a custom chip for a selected value. Receives the value, its matching option (if any), and an `onRemove` callback."
    },
    {
      "name": "renderOption",
      "optional": true,
      "type": "( option: MultiSelectOption, isSelected: boolean, isHighlighted: boolean, ) => ReactNode",
      "doc": "Render a custom row in the popover. Default renders label + sub + checkmark when selected."
    },
    {
      "name": "maxPopoverHeight",
      "optional": true,
      "type": "number",
      "doc": "Maximum height of the option popover. Default `240px`."
    },
    {
      "name": "closeOnSelect",
      "optional": true,
      "type": "boolean",
      "doc": "Disable closing the popover when an option is picked. Default `false` (kept open) — multi-select picking is iterative and auto-close is rarely what you want."
    },
    {
      "name": "className",
      "optional": true,
      "type": "string",
      "doc": "Class on the wrapper."
    },
    {
      "name": "id",
      "optional": true,
      "type": "string",
      "doc": "ID on the input (for label association)."
    },
    {
      "name": "ariaLabel",
      "optional": true,
      "type": "string",
      "doc": "Accessible label when no `<label>` is paired."
    }
  ],
  "classesUsed": [
    "combobox",
    "multi-select",
    "multi-select-chevron",
    "multi-select-chip",
    "multi-select-chip-label",
    "multi-select-chip-overflow",
    "multi-select-chip-remove",
    "multi-select-control",
    "multi-select-empty",
    "multi-select-input",
    "multi-select-option",
    "multi-select-option-body",
    "multi-select-option-check",
    "multi-select-option-label",
    "multi-select-option-sub",
    "multi-select-popover"
  ],
  "examples": {
    "react": "const [tags, setTags] = useState<string[]>([]);",
    "html": "<div class=\"multi-select\" style=\"min-width:280px;\">\n  <div class=\"multi-select-control\">\n    <span class=\"multi-select-chip\">\n      <span class=\"multi-select-chip-label\">Design</span>\n      <button type=\"button\" class=\"multi-select-chip-remove\" aria-label=\"Remove Design\">×</button>\n    </span>\n    <input class=\"multi-select-input\" placeholder=\"Pick tags…\">\n    <span class=\"multi-select-chevron\" aria-hidden=\"true\">▾</span>\n  </div>\n</div>",
    "css": ".combobox { display: inline-flex; flex-direction: column; min-width: 220px; }\n\n.multi-select {\n  position: relative;\n  font: inherit;\n}\n\n.multi-select-chevron {\n  display: inline-flex;\n  align-items: center;\n  color: var(--fg-dim);\n  margin-left: auto;\n  transition: transform var(--dur-2) var(--ease);\n}\n\n.multi-select-chip {\n  display: inline-flex;\n  align-items: center;\n  gap: 4px;\n  padding: 2px 4px 2px 8px;\n  background: var(--accent-soft);\n  color: var(--accent-text);\n  border-radius: var(--r-sm);\n  font: 500 12.5px/1.4 var(--f-body);\n  white-space: nowrap;\n  max-width: 100%;\n}\n\n.multi-select-chip-label { overflow: hidden; text-overflow: ellipsis; }\n\n.multi-select-chip-overflow {\n  background: var(--bg-warm);\n  color: var(--fg-dim);\n  padding: 2px 8px;\n}\n\n.multi-select-chip-remove {\n  display: inline-flex;\n  align-items: center;\n  justify-content: center;\n  width: 18px;\n  height: 18px;\n  border: 0;\n  background: transparent;\n  color: inherit;\n  opacity: 0.7;\n  border-radius: var(--r-xs);\n  cursor: pointer;\n  transition: background var(--dur-1) var(--ease), opacity var(--dur-1) var(--ease);\n}\n\n.multi-select-control {\n  display: flex;\n  align-items: center;\n  flex-wrap: wrap;\n  gap: 4px;\n  min-height: 38px;\n  padding: 4px 8px 4px 6px;\n  background: var(--bg-paper);\n  border: 1px solid var(--hair);\n  border-radius: var(--r-md);\n  cursor: text;\n  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);\n}\n\n.multi-select-empty {\n  display: block;\n  padding: var(--s-3) var(--s-4);\n  font-size: 13px;\n  color: var(--fg-dim);\n  text-align: center;\n}\n\n.multi-select-input {\n  flex: 1;\n  min-width: 80px;\n  border: 0;\n  background: transparent;\n  outline: none;\n  padding: 4px 4px;\n  font: inherit;\n  color: var(--fg);\n}\n\n.multi-select-option {\n  display: flex;\n  align-items: center;\n  gap: var(--s-3);\n  padding: 6px 10px;\n  border-radius: var(--r-sm);\n  cursor: pointer;\n  font: inherit;\n  color: var(--fg);\n  transition: background var(--dur-1) var(--ease);\n}\n\n.multi-select-option-body {\n  flex: 1;\n  min-width: 0;\n  display: flex;\n  flex-direction: column;\n  gap: 2px;\n}\n\n.multi-select-option-check {\n  display: inline-flex;\n  align-items: center;\n  color: var(--accent);\n}\n\n.multi-select-option-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n.multi-select-option-sub {\n  color: var(--fg-faint);\n  font: 400 12px/1.3 var(--f-mono);\n}\n\n.multi-select-popover {\n  position: absolute;\n  top: 100%;\n  left: 0;\n  right: 0;\n  margin-top: 4px;\n  z-index: var(--z-overlay, 40);\n  background: var(--bg-paper);\n  border: 1px solid var(--hair);\n  border-radius: var(--r-md);\n  box-shadow: var(--sh-2);\n  list-style: none;\n  margin-left: 0;\n  margin-right: 0;\n  margin-bottom: 0;\n  padding: 4px;\n  overflow-y: auto;\n  display: flex;\n  flex-direction: column;\n  gap: 1px;\n}"
  }
}
