{
  "name": "combobox",
  "type": "registry:component",
  "files": [
    {
      "name": "combobox.component.ts",
      "content": "import {\n  booleanAttribute,\n  ChangeDetectionStrategy,\n  Component,\n  computed,\n  contentChild,\n  effect,\n  ElementRef,\n  forwardRef,\n  inject,\n  input,\n  linkedSignal,\n  model,\n  numberAttribute,\n  output,\n  signal,\n  untracked,\n  ViewEncapsulation,\n} from '@angular/core';\nimport { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nimport { NgIcon } from '@ng-icons/core';\nimport type { ClassValue } from 'clsx';\n\nimport type { ZardButtonTypeVariants } from '@/shared/components/button';\nimport { comboboxValueVariants, comboboxVariants } from '@/shared/components/combobox/combobox.variants';\nimport { mergeClasses, noopFn } from '@/shared/utils/merge-classes';\n\nimport {\n  ZardComboboxContentComponent,\n  ZardComboboxEmptyComponent,\n  ZardComboboxListComponent,\n} from './combobox-content.component';\nimport { ZardComboboxGroupComponent, ZardComboboxLabelComponent } from './combobox-group.component';\nimport { ZardComboboxInputComponent } from './combobox-input.component';\nimport { ZardComboboxItemComponent } from './combobox-item.component';\nimport {\n  type ZardComboboxAlignVariants,\n  type ZardComboboxAnchorKind,\n  type ZardComboboxFilterVariants,\n  type ZardComboboxGroup,\n  type ZardComboboxItemRef,\n  type ZardComboboxOption,\n  ZardComboboxRoot,\n  type ZardComboboxSideVariants,\n} from './combobox.types';\nimport { type ZardComboboxWidthVariants } from './combobox.variants';\n\nexport type { ZardComboboxGroup, ZardComboboxOption } from './combobox.types';\n\ntype OnChangeType = (value: string | string[] | null) => void;\ntype OnTouchedType = () => void;\n\nlet nextComboboxId = 0;\n\n@Component({\n  selector: 'z-combobox, [z-combobox]',\n  imports: [\n    NgIcon,\n    ZardComboboxContentComponent,\n    ZardComboboxEmptyComponent,\n    ZardComboboxGroupComponent,\n    ZardComboboxInputComponent,\n    ZardComboboxItemComponent,\n    ZardComboboxLabelComponent,\n    ZardComboboxListComponent,\n  ],\n  template: `\n    <ng-content />\n\n    @if (!projectedContent()) {\n      <z-combobox-input />\n\n      <z-combobox-content>\n        <z-combobox-empty>{{ emptyText() }}</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (group of groups(); track group.label ?? $index) {\n            <z-combobox-group>\n              @if (group.label) {\n                <z-combobox-label>{{ group.label }}</z-combobox-label>\n              }\n              @for (option of group.options; track option.value) {\n                <z-combobox-item [zValue]=\"option.value\" [zLabel]=\"option.label\" [zDisabled]=\"option.disabled ?? false\">\n                  @if (option.icon; as icon) {\n                    <ng-icon [name]=\"icon\" />\n                  }\n                  {{ option.label }}\n                </z-combobox-item>\n              }\n            </z-combobox-group>\n          } @empty {\n            @for (option of options(); track option.value) {\n              <z-combobox-item [zValue]=\"option.value\" [zLabel]=\"option.label\" [zDisabled]=\"option.disabled ?? false\">\n                @if (option.icon; as icon) {\n                  <ng-icon [name]=\"icon\" />\n                }\n                {{ option.label }}\n              </z-combobox-item>\n            }\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    }\n  `,\n  providers: [\n    {\n      provide: NG_VALUE_ACCESSOR,\n      useExisting: forwardRef(() => ZardComboboxComponent),\n      multi: true,\n    },\n    {\n      provide: ZardComboboxRoot,\n      useExisting: forwardRef(() => ZardComboboxComponent),\n    },\n  ],\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  encapsulation: ViewEncapsulation.None,\n  host: {\n    'data-slot': 'combobox',\n    '[attr.data-auto-highlight]': 'zAutoHighlight() ? \"\" : null',\n    '[attr.data-disabled]': 'disabled() ? \"\" : null',\n    '[attr.data-invalid]': 'zInvalid() ? \"\" : null',\n    '[attr.data-multiple]': 'zMultiple() ? \"\" : null',\n    '[attr.data-state]': 'open() ? \"open\" : \"closed\"',\n    '[class]': 'classes()',\n    '(keydown)': 'handleKeydown($event)',\n  },\n  exportAs: 'zCombobox',\n})\nexport class ZardComboboxComponent implements ControlValueAccessor, ZardComboboxRoot {\n  private readonly elementRef = inject(ElementRef<HTMLElement>);\n\n  readonly class = input<ClassValue>('');\n  readonly zWidth = input<ZardComboboxWidthVariants>('default');\n  readonly placeholder = input<string>('Select...');\n  readonly searchPlaceholder = input<string>('Search...');\n  readonly emptyText = input<string>('No results found.');\n  readonly zDisabled = input(false, { transform: booleanAttribute });\n  readonly searchable = input(true, { transform: booleanAttribute });\n  readonly options = input<ZardComboboxOption[]>([]);\n  readonly groups = input<ZardComboboxGroup[]>([]);\n  readonly ariaLabel = input<string>('');\n  readonly ariaDescribedBy = input<string>('');\n\n  /**\n   * Legacy value input kept for backwards compatibility. It is synchronised into `zValue`.\n   * Prefer the two-way `[(zValue)]` binding.\n   */\n  readonly value = input<string | null>(null);\n\n  /**\n   * @deprecated The trigger is no longer a `z-button`, so this input has no visual effect.\n   * It is kept only so existing templates keep compiling.\n   */\n  readonly buttonVariant = input<ZardButtonTypeVariants>('outline');\n\n  readonly zValue = model<string | string[] | null>(null);\n  readonly zOpen = model(false);\n  readonly zMultiple = input(false, { transform: booleanAttribute });\n  readonly zFilter = input<ZardComboboxFilterVariants>('contains');\n  readonly zFilterFn = input<((label: string, query: string) => boolean) | null>(null);\n  readonly zSide = input<ZardComboboxSideVariants>('bottom');\n  readonly zAlign = input<ZardComboboxAlignVariants>('start');\n  readonly zSideOffset = input(6, { transform: numberAttribute });\n  readonly zAlignOffset = input(0, { transform: numberAttribute });\n  readonly zAutoHighlight = input(false, { transform: booleanAttribute });\n  readonly zInvalid = input(false, { transform: booleanAttribute });\n\n  readonly zComboSelected = output<ZardComboboxOption>();\n  readonly zQueryChange = output<string>();\n\n  readonly inputId = `z-combobox-input-${nextComboboxId}`;\n  readonly listboxId = `z-combobox-listbox-${nextComboboxId++}`;\n\n  protected readonly projectedContent = contentChild(ZardComboboxContentComponent);\n\n  readonly disabled = linkedSignal(() => this.zDisabled());\n  readonly open = this.zOpen.asReadonly();\n  readonly query = signal('');\n\n  private readonly items = signal<readonly ZardComboboxItemRef[]>([]);\n  private readonly highlightedIndex = signal(-1);\n  private readonly inputElement = signal<HTMLInputElement | null>(null);\n  private readonly inputAnchor = signal<HTMLElement | null>(null);\n  private readonly chipsAnchor = signal<HTMLElement | null>(null);\n  private readonly triggerAnchor = signal<HTMLElement | null>(null);\n  private ignoreFocusOpen = false;\n  private legacyValueSynced = false;\n\n  readonly hasChips = computed(() => this.chipsAnchor() !== null);\n  readonly anchorElement = computed<HTMLElement | null>(\n    () => this.chipsAnchor() ?? this.inputAnchor() ?? this.triggerAnchor() ?? this.elementRef.nativeElement,\n  );\n\n  readonly selectedValues = computed<readonly string[]>(() => {\n    const value = this.zValue();\n    if (value === null || value === undefined) {\n      return [];\n    }\n    return Array.isArray(value) ? value : [value];\n  });\n\n  readonly hasValue = computed(() => this.selectedValues().length > 0);\n\n  readonly visibleItems = computed<readonly ZardComboboxItemRef[]>(() =>\n    this.items().filter(item => this.matchesQuery(item.label())),\n  );\n\n  readonly highlightedItem = computed<ZardComboboxItemRef | null>(() => {\n    const index = this.highlightedIndex();\n    return index < 0 ? null : (this.visibleItems()[index] ?? null);\n  });\n\n  readonly selectedLabel = computed(() =>\n    this.selectedValues()\n      .map(value => this.labelOf(value))\n      .join(', '),\n  );\n\n  protected readonly classes = computed(() => mergeClasses(comboboxVariants({ zWidth: this.zWidth() }), this.class()));\n\n  private onChange: OnChangeType = noopFn;\n  private onTouched: OnTouchedType = noopFn;\n\n  constructor() {\n    effect(() => {\n      const legacyValue = this.value();\n      untracked(() => {\n        if (!this.legacyValueSynced) {\n          this.legacyValueSynced = true;\n          if (legacyValue === null) {\n            return;\n          }\n        }\n        this.zValue.set(legacyValue);\n      });\n    });\n\n    effect(() => {\n      if (this.disabled() && this.open()) {\n        untracked(() => this.closePanel());\n      }\n    });\n  }\n\n  matchesQuery(label: string): boolean {\n    const rawQuery = this.query();\n    const query = rawQuery.trim();\n\n    if (!query || !this.searchable()) {\n      return true;\n    }\n\n    const filterFn = this.zFilterFn();\n    if (filterFn) {\n      return filterFn(label, rawQuery);\n    }\n\n    const mode = this.zFilter();\n    if (mode === 'none') {\n      return true;\n    }\n\n    const haystack = label.toLowerCase();\n    const needle = query.toLowerCase();\n\n    return mode === 'startsWith' ? haystack.startsWith(needle) : haystack.includes(needle);\n  }\n\n  isSelected(value: string): boolean {\n    return this.selectedValues().includes(value);\n  }\n\n  labelOf(value: string): string {\n    const option = this.findOption(value);\n    if (option) {\n      return option.label;\n    }\n\n    const item = this.items().find(candidate => candidate.zValue() === value);\n    return item?.label() ?? value;\n  }\n\n  registerItem(item: ZardComboboxItemRef): void {\n    this.items.update(items => [...items, item]);\n  }\n\n  unregisterItem(item: ZardComboboxItemRef): void {\n    this.items.update(items => items.filter(candidate => candidate !== item));\n  }\n\n  registerInput(element: HTMLInputElement | null): void {\n    if (!element && this.inputElement()) {\n      return;\n    }\n    this.inputElement.set(element);\n  }\n\n  registerAnchor(element: HTMLElement, kind: ZardComboboxAnchorKind): void {\n    if (kind === 'chips') {\n      this.chipsAnchor.set(element);\n      return;\n    }\n\n    if (kind === 'trigger') {\n      this.triggerAnchor.set(element);\n      return;\n    }\n\n    this.inputAnchor.set(element);\n  }\n\n  openPanel(): void {\n    if (this.ignoreFocusOpen || this.disabled() || this.open()) {\n      return;\n    }\n\n    this.query.set('');\n    this.zOpen.set(true);\n    this.highlightedIndex.set(this.visibleItems().findIndex(item => this.isSelected(item.zValue())));\n  }\n\n  closePanel(): void {\n    if (!this.open()) {\n      return;\n    }\n\n    // In popup mode the input lives inside the overlay and is destroyed with it,\n    // so the focus goes back to the trigger that owns the popup.\n    const restoreTriggerFocus = this.popupOwnsInput();\n\n    this.zOpen.set(false);\n    this.highlightedIndex.set(-1);\n    this.query.set('');\n\n    if (restoreTriggerFocus) {\n      this.inputElement.set(null);\n      this.triggerAnchor()?.focus();\n    }\n  }\n\n  setQuery(query: string): void {\n    this.query.set(query);\n\n    if (this.zAutoHighlight()) {\n      this.highlightEdge('first');\n    } else {\n      this.highlightedIndex.set(-1);\n    }\n\n    this.zQueryChange.emit(query);\n  }\n\n  highlightItem(item: ZardComboboxItemRef | null): void {\n    if (!item) {\n      this.highlightedIndex.set(-1);\n      return;\n    }\n\n    this.highlightedIndex.set(this.visibleItems().indexOf(item));\n    item.element.scrollIntoView?.({ block: 'nearest' });\n  }\n\n  selectItem(item: ZardComboboxItemRef): void {\n    if (this.disabled() || item.zDisabled()) {\n      return;\n    }\n\n    const value = item.zValue();\n\n    if (this.zMultiple()) {\n      const current = this.selectedValues();\n      const isAdding = !current.includes(value);\n      this.commit(isAdding ? [...current, value] : current.filter(candidate => candidate !== value));\n      this.setQuery('');\n      if (isAdding) {\n        this.emitSelected(value, item);\n      }\n      this.highlightItem(item);\n      this.focusInput();\n      return;\n    }\n\n    const nextValue = this.isSelected(value) ? null : value;\n    this.commit(nextValue);\n    if (nextValue !== null) {\n      this.emitSelected(value, item);\n    }\n    this.closePanel();\n    this.focusInput();\n  }\n\n  clear(): void {\n    if (this.disabled()) {\n      return;\n    }\n\n    this.commit(this.zMultiple() ? [] : null);\n    this.query.set('');\n  }\n\n  removeValue(value: string): void {\n    if (this.disabled()) {\n      return;\n    }\n\n    if (this.zMultiple()) {\n      this.commit(this.selectedValues().filter(candidate => candidate !== value));\n      return;\n    }\n\n    if (this.isSelected(value)) {\n      this.commit(null);\n    }\n  }\n\n  removeLastValue(): void {\n    const values = this.selectedValues();\n    const last = values.at(-1);\n    if (last !== undefined) {\n      this.removeValue(last);\n    }\n  }\n\n  focusInput(): void {\n    const element = this.inputElement();\n    if (!element) {\n      return;\n    }\n\n    this.ignoreFocusOpen = true;\n    element.focus();\n    this.ignoreFocusOpen = false;\n  }\n\n  /** True when the registered input is rendered inside the overlay instead of next to the root. */\n  private popupOwnsInput(): boolean {\n    const element = this.inputElement();\n    return !!element && !!this.triggerAnchor() && !this.elementRef.nativeElement.contains(element);\n  }\n\n  markAsTouched(): void {\n    this.onTouched();\n  }\n\n  /**\n   * Public because the popup lives in the CDK overlay: keystrokes typed inside it never bubble up\n   * to the root host, so `z-combobox-content` forwards them here.\n   */\n  handleKeydown(event: KeyboardEvent): void {\n    if (this.disabled()) {\n      return;\n    }\n\n    // A standalone trigger already toggles through its native click, which Enter and Space fire.\n    if ((event.key === 'Enter' || event.key === ' ') && event.target === this.triggerAnchor()) {\n      return;\n    }\n\n    if (this.open()) {\n      this.onKeydownWhileOpen(event);\n      return;\n    }\n\n    switch (event.key) {\n      case 'ArrowDown':\n        event.preventDefault();\n        this.openPanel();\n        this.highlightEdge('first');\n        break;\n      case 'ArrowUp':\n        event.preventDefault();\n        this.openPanel();\n        this.highlightEdge('last');\n        break;\n      case 'Enter':\n        event.preventDefault();\n        this.openPanel();\n        break;\n      case 'Escape':\n        if (this.hasValue()) {\n          event.preventDefault();\n          this.clear();\n        }\n        break;\n    }\n  }\n\n  private onKeydownWhileOpen(event: KeyboardEvent): void {\n    switch (event.key) {\n      case 'ArrowDown':\n        event.preventDefault();\n        this.moveHighlight(1);\n        break;\n      case 'ArrowUp':\n        event.preventDefault();\n        this.moveHighlight(-1);\n        break;\n      case 'Home':\n        event.preventDefault();\n        this.highlightEdge('first');\n        break;\n      case 'End':\n        event.preventDefault();\n        this.highlightEdge('last');\n        break;\n      case 'Enter': {\n        event.preventDefault();\n        const item = this.highlightedItem();\n        if (item) {\n          this.selectItem(item);\n        }\n        break;\n      }\n      case 'Escape':\n        event.preventDefault();\n        this.closePanel();\n        this.focusInput();\n        break;\n      case 'Tab':\n        this.closePanel();\n        break;\n    }\n  }\n\n  private moveHighlight(direction: 1 | -1): void {\n    const items = this.selectableItems();\n    if (items.length === 0) {\n      return;\n    }\n\n    const current = this.highlightedItem();\n    const currentIndex = current ? items.indexOf(current) : -1;\n    let nextIndex = currentIndex + direction;\n\n    if (nextIndex < 0) {\n      nextIndex = items.length - 1;\n    } else if (nextIndex >= items.length) {\n      nextIndex = 0;\n    }\n\n    this.highlightItem(items[nextIndex]);\n  }\n\n  private highlightEdge(edge: 'first' | 'last'): void {\n    const items = this.selectableItems();\n    if (items.length === 0) {\n      return;\n    }\n\n    this.highlightItem(edge === 'first' ? items[0] : items[items.length - 1]);\n  }\n\n  private selectableItems(): ZardComboboxItemRef[] {\n    return this.visibleItems().filter(item => !item.zDisabled());\n  }\n\n  private commit(value: string | string[] | null): void {\n    this.zValue.set(value);\n    this.onChange(value);\n  }\n\n  private emitSelected(value: string, item: ZardComboboxItemRef): void {\n    this.zComboSelected.emit(this.findOption(value) ?? { value, label: item.label(), disabled: item.zDisabled() });\n  }\n\n  private findOption(value: string): ZardComboboxOption | undefined {\n    for (const group of this.groups()) {\n      const found = group.options.find(option => option.value === value);\n      if (found) {\n        return found;\n      }\n    }\n\n    return this.options().find(option => option.value === value);\n  }\n\n  writeValue(value: string | string[] | null): void {\n    if (this.zMultiple()) {\n      this.zValue.set(Array.isArray(value) ? value : value ? [value] : []);\n      return;\n    }\n\n    this.zValue.set(Array.isArray(value) ? (value[0] ?? null) : (value ?? null));\n  }\n\n  registerOnChange(fn: OnChangeType): void {\n    this.onChange = fn;\n  }\n\n  registerOnTouched(fn: OnTouchedType): void {\n    this.onTouched = fn;\n  }\n\n  setDisabledState(isDisabled: boolean): void {\n    this.disabled.set(isDisabled);\n    if (isDisabled) {\n      this.closePanel();\n    }\n  }\n}\n\n@Component({\n  selector: 'z-combobox-value, [z-combobox-value]',\n  template: '{{ text() }}',\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  encapsulation: ViewEncapsulation.None,\n  host: {\n    'data-slot': 'combobox-value',\n    '[attr.data-placeholder]': 'root.hasValue() ? null : \"\"',\n    '[class]': 'classes()',\n  },\n  exportAs: 'zComboboxValue',\n})\nexport class ZardComboboxValueComponent {\n  protected readonly root = inject(ZardComboboxRoot);\n\n  readonly class = input<ClassValue>('');\n  readonly placeholder = input<string>('');\n\n  protected readonly text = computed(() => this.root.selectedLabel() || this.placeholder() || this.root.placeholder());\n  protected readonly classes = computed(() => mergeClasses(comboboxValueVariants(), this.class()));\n}\n"
    },
    {
      "name": "combobox.variants.ts",
      "content": "import { cva, type VariantProps } from 'class-variance-authority';\n\nimport { mergeClasses } from '@/shared/utils/merge-classes';\n\nexport const comboboxVariants = cva('group/combobox relative block', {\n  variants: {\n    zWidth: {\n      default: 'w-50',\n      sm: 'w-37.5',\n      md: 'w-62.5',\n      lg: 'w-87.5',\n      full: 'w-full',\n    },\n  },\n  defaultVariants: {\n    zWidth: 'default',\n  },\n});\n\nexport const comboboxValueVariants = cva('block truncate text-sm');\n\n/**\n * `contents` keeps the host out of the layout, so the inner `z-input-group` behaves as a direct\n * child of whatever wraps the input — the root or, in popup mode, the content popup, whose\n * `*:data-[slot=input-group]:*` rules would otherwise never reach it.\n */\nexport const comboboxInputHostVariants = cva('contents');\n\nexport const comboboxInputGroupVariants = cva('w-auto');\n\nexport const comboboxTriggerVariants = cva(\"[&_svg:not([class*='size-'])]:size-4\", {\n  variants: {\n    /** A standalone trigger lives outside a `z-input-group`, so the input-group-only rules do not apply. */\n    zStandalone: {\n      false: 'group-has-data-[slot=combobox-clear]/input-group:hidden aria-expanded:bg-transparent',\n      true: '',\n    },\n  },\n  defaultVariants: {\n    zStandalone: false,\n  },\n});\n\nexport const comboboxClearVariants = cva('');\n\nexport const comboboxContentVariants = cva(\n  mergeClasses(\n    'group/combobox-content relative max-h-(--z-combobox-available-height) w-(--z-combobox-anchor-width)',\n    'max-w-(--z-combobox-available-width) min-w-(--z-combobox-anchor-width)',\n    'origin-(--z-combobox-transform-origin) overflow-hidden rounded-lg bg-popover text-popover-foreground',\n    'shadow-md ring-1 ring-foreground/10 duration-100',\n    'data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2',\n    '*:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8',\n    '*:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30',\n    '*:data-[slot=input-group]:shadow-none',\n    'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',\n    'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n  ),\n);\n\nexport const comboboxListVariants = cva(\n  mergeClasses(\n    'no-scrollbar block scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0',\n    'max-h-[min(calc(--spacing(72)-(--spacing(9))),calc(var(--z-combobox-available-height)-(--spacing(9))))]',\n  ),\n);\n\nexport const comboboxItemVariants = cva(\n  mergeClasses(\n    'relative flex w-full cursor-default items-center gap-2 rounded-md py-1 pe-8 ps-1.5 text-sm',\n    'outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground',\n    'not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground',\n    'data-disabled:pointer-events-none data-disabled:opacity-50',\n    \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n  ),\n  {\n    variants: {\n      zVariant: {\n        default: '',\n        destructive: 'text-destructive data-highlighted:bg-destructive/10 data-highlighted:text-destructive',\n      },\n    },\n    defaultVariants: {\n      zVariant: 'default',\n    },\n  },\n);\n\nexport const comboboxItemIndicatorVariants = cva(\n  'pointer-events-none absolute end-2 flex size-4 items-center justify-center',\n);\n\nexport const comboboxGroupVariants = cva('block');\n\nexport const comboboxLabelVariants = cva('block px-2 py-1.5 text-xs text-muted-foreground');\n\nexport const comboboxEmptyVariants = cva(\n  mergeClasses(\n    'hidden w-full justify-center py-2 text-center text-sm text-muted-foreground',\n    'group-data-empty/combobox-content:flex',\n  ),\n);\n\nexport const comboboxSeparatorVariants = cva('-mx-1 my-1 block h-px bg-border');\n\nexport const comboboxChipsVariants = cva(\n  mergeClasses(\n    'flex min-h-8 flex-wrap items-center gap-1 rounded-lg border border-input bg-transparent bg-clip-padding',\n    'px-2.5 py-1 text-sm transition-colors focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50',\n    'has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20',\n    'has-data-[slot=combobox-chip]:px-1 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50',\n    'dark:has-aria-invalid:ring-destructive/40',\n  ),\n);\n\nexport const comboboxChipVariants = cva(\n  mergeClasses(\n    'flex h-[calc(--spacing(5.25))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs',\n    'font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none',\n    'has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pe-0',\n  ),\n);\n\nexport const comboboxChipRemoveVariants = cva('-ms-1 opacity-50 hover:opacity-100');\n\nexport const comboboxChipsInputVariants = cva('min-w-16 flex-1 bg-transparent outline-none');\n\nexport type ZardComboboxWidthVariants = NonNullable<VariantProps<typeof comboboxVariants>['zWidth']>;\nexport type ZardComboboxItemVariants = NonNullable<VariantProps<typeof comboboxItemVariants>['zVariant']>;\nexport type ZardComboboxTriggerStandaloneVariants = NonNullable<\n  VariantProps<typeof comboboxTriggerVariants>['zStandalone']\n>;\n"
    },
    {
      "name": "index.ts",
      "content": "export * from './combobox-chips.component';\nexport * from './combobox-content.component';\nexport * from './combobox-group.component';\nexport * from './combobox-input.component';\nexport * from './combobox-item.component';\nexport * from './combobox.component';\nexport * from './combobox.imports';\nexport * from './combobox.types';\nexport * from './combobox.variants';\n"
    }
  ],
  "registryDependencies": [
    "button",
    "command",
    "popover",
    "empty",
    "input"
  ],
  "demos": [
    {
      "name": "auto-highlight.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardComboboxImports } from '../combobox.imports';\nimport type { ZardComboboxOption } from '../combobox.types';\n\n@Component({\n  selector: 'zard-demo-combobox-auto-highlight',\n  imports: [ZardComboboxImports],\n  standalone: true,\n  template: `\n    <z-combobox zAutoHighlight [(zValue)]=\"value\">\n      <z-combobox-input placeholder=\"Select a framework\" />\n\n      <z-combobox-content>\n        <z-combobox-empty>No items found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (framework of frameworks; track framework.value) {\n            <z-combobox-item [zValue]=\"framework.value\">{{ framework.label }}</z-combobox-item>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n})\nexport class ZardDemoComboboxAutoHighlightComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  frameworks: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React' },\n    { value: 'vue', label: 'Vue.js' },\n    { value: 'svelte', label: 'Svelte' },\n    { value: 'ember', label: 'Ember.js' },\n    { value: 'nextjs', label: 'Next.js' },\n  ];\n}\n"
    },
    {
      "name": "clear.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardComboboxImports } from '../combobox.imports';\nimport type { ZardComboboxOption } from '../combobox.types';\n\n@Component({\n  selector: 'zard-demo-combobox-clear',\n  imports: [ZardComboboxImports],\n  standalone: true,\n  template: `\n    <z-combobox [(zValue)]=\"value\">\n      <z-combobox-input zShowClear placeholder=\"Select a framework\" />\n\n      <z-combobox-content>\n        <z-combobox-empty>No items found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (framework of frameworks; track framework.value) {\n            <z-combobox-item [zValue]=\"framework.value\">{{ framework.label }}</z-combobox-item>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n})\nexport class ZardDemoComboboxClearComponent {\n  readonly value = signal<string | string[] | null>('angular');\n\n  frameworks: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React' },\n    { value: 'vue', label: 'Vue.js' },\n    { value: 'svelte', label: 'Svelte' },\n    { value: 'ember', label: 'Ember.js' },\n    { value: 'nextjs', label: 'Next.js' },\n  ];\n}\n"
    },
    {
      "name": "custom-items.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardItemImports } from '../../item/item.imports';\nimport { ZardComboboxImports } from '../combobox.imports';\n\n@Component({\n  selector: 'zard-demo-combobox-custom-items',\n  imports: [ZardComboboxImports, ZardItemImports],\n  standalone: true,\n  template: `\n    <z-combobox zWidth=\"md\" [(zValue)]=\"value\">\n      <z-combobox-input placeholder=\"Search countries...\" />\n\n      <z-combobox-content>\n        <z-combobox-empty>No countries found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (country of countries; track country.code) {\n            <z-combobox-item [zValue]=\"country.value\" [zLabel]=\"country.label\">\n              <div z-item zSize=\"xs\" class=\"p-0\">\n                <div z-item-content>\n                  <div z-item-title class=\"whitespace-nowrap\">{{ country.label }}</div>\n                  <p z-item-description>{{ country.continent }} ({{ country.code }})</p>\n                </div>\n              </div>\n            </z-combobox-item>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n})\nexport class ZardDemoComboboxCustomItemsComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  readonly countries = [\n    { code: 'ar', value: 'argentina', label: 'Argentina', continent: 'South America' },\n    { code: 'au', value: 'australia', label: 'Australia', continent: 'Oceania' },\n    { code: 'br', value: 'brazil', label: 'Brazil', continent: 'South America' },\n    { code: 'ca', value: 'canada', label: 'Canada', continent: 'North America' },\n    { code: 'cn', value: 'china', label: 'China', continent: 'Asia' },\n    { code: 'co', value: 'colombia', label: 'Colombia', continent: 'South America' },\n    { code: 'eg', value: 'egypt', label: 'Egypt', continent: 'Africa' },\n    { code: 'fr', value: 'france', label: 'France', continent: 'Europe' },\n    { code: 'de', value: 'germany', label: 'Germany', continent: 'Europe' },\n    { code: 'it', value: 'italy', label: 'Italy', continent: 'Europe' },\n    { code: 'jp', value: 'japan', label: 'Japan', continent: 'Asia' },\n    { code: 'ke', value: 'kenya', label: 'Kenya', continent: 'Africa' },\n    { code: 'mx', value: 'mexico', label: 'Mexico', continent: 'North America' },\n    { code: 'nz', value: 'new-zealand', label: 'New Zealand', continent: 'Oceania' },\n    { code: 'ng', value: 'nigeria', label: 'Nigeria', continent: 'Africa' },\n    { code: 'za', value: 'south-africa', label: 'South Africa', continent: 'Africa' },\n    { code: 'kr', value: 'south-korea', label: 'South Korea', continent: 'Asia' },\n    { code: 'gb', value: 'united-kingdom', label: 'United Kingdom', continent: 'Europe' },\n    { code: 'us', value: 'united-states', label: 'United States', continent: 'North America' },\n  ];\n}\n"
    },
    {
      "name": "default.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardComboboxImports } from '../combobox.imports';\nimport type { ZardComboboxOption } from '../combobox.types';\n\n@Component({\n  selector: 'zard-demo-combobox-default',\n  imports: [ZardComboboxImports],\n  standalone: true,\n  template: `\n    <z-combobox [(zValue)]=\"value\">\n      <z-combobox-input placeholder=\"Select a framework\" />\n\n      <z-combobox-content>\n        <z-combobox-empty>No items found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (framework of frameworks; track framework.value) {\n            <z-combobox-item [zValue]=\"framework.value\">{{ framework.label }}</z-combobox-item>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n})\nexport class ZardDemoComboboxDefaultComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  frameworks: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React' },\n    { value: 'vue', label: 'Vue.js' },\n    { value: 'svelte', label: 'Svelte' },\n    { value: 'ember', label: 'Ember.js' },\n    { value: 'nextjs', label: 'Next.js' },\n  ];\n}\n"
    },
    {
      "name": "disabled.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardComboboxImports } from '../combobox.imports';\nimport type { ZardComboboxOption } from '../combobox.types';\n\n@Component({\n  selector: 'zard-demo-combobox-disabled',\n  imports: [ZardComboboxImports],\n  standalone: true,\n  template: `\n    <div class=\"flex flex-wrap gap-4\">\n      <z-combobox zDisabled>\n        <z-combobox-input placeholder=\"Select a framework\" />\n\n        <z-combobox-content>\n          <z-combobox-empty>No items found.</z-combobox-empty>\n\n          <z-combobox-list>\n            @for (framework of frameworks; track framework.value) {\n              <z-combobox-item [zValue]=\"framework.value\">{{ framework.label }}</z-combobox-item>\n            }\n          </z-combobox-list>\n        </z-combobox-content>\n      </z-combobox>\n\n      <z-combobox [(zValue)]=\"value\">\n        <z-combobox-input placeholder=\"Select a framework\" />\n\n        <z-combobox-content>\n          <z-combobox-empty>No items found.</z-combobox-empty>\n\n          <z-combobox-list>\n            @for (framework of frameworksWithDisabled; track framework.value) {\n              <z-combobox-item [zValue]=\"framework.value\" [zDisabled]=\"framework.disabled ?? false\">\n                {{ framework.label }}\n              </z-combobox-item>\n            }\n          </z-combobox-list>\n        </z-combobox-content>\n      </z-combobox>\n    </div>\n  `,\n})\nexport class ZardDemoComboboxDisabledComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  frameworks: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React' },\n    { value: 'vue', label: 'Vue.js' },\n  ];\n\n  frameworksWithDisabled: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React', disabled: true },\n    { value: 'vue', label: 'Vue.js' },\n    { value: 'svelte', label: 'Svelte', disabled: true },\n    { value: 'ember', label: 'Ember.js' },\n  ];\n}\n"
    },
    {
      "name": "grouped.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardComboboxImports } from '../combobox.imports';\n\n@Component({\n  selector: 'zard-demo-combobox-grouped',\n  imports: [ZardComboboxImports],\n  standalone: true,\n  template: `\n    <z-combobox zWidth=\"md\" [(zValue)]=\"value\">\n      <z-combobox-input placeholder=\"Select a timezone\" />\n\n      <z-combobox-content>\n        <z-combobox-empty>No timezones found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (group of timezones; track group.label; let last = $last) {\n            <z-combobox-group>\n              <z-combobox-label>{{ group.label }}</z-combobox-label>\n\n              @for (zone of group.options; track zone) {\n                <z-combobox-item [zValue]=\"zone\">{{ zone }}</z-combobox-item>\n              }\n\n              @if (!last) {\n                <z-combobox-separator />\n              }\n            </z-combobox-group>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n})\nexport class ZardDemoComboboxGroupedComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  readonly timezones = [\n    {\n      label: 'Americas',\n      options: [\n        '(GMT-5) New York',\n        '(GMT-8) Los Angeles',\n        '(GMT-6) Chicago',\n        '(GMT-5) Toronto',\n        '(GMT-8) Vancouver',\n        '(GMT-3) São Paulo',\n      ],\n    },\n    {\n      label: 'Europe',\n      options: [\n        '(GMT+0) London',\n        '(GMT+1) Paris',\n        '(GMT+1) Berlin',\n        '(GMT+1) Rome',\n        '(GMT+1) Madrid',\n        '(GMT+1) Amsterdam',\n      ],\n    },\n    {\n      label: 'Asia/Pacific',\n      options: [\n        '(GMT+9) Tokyo',\n        '(GMT+8) Shanghai',\n        '(GMT+8) Singapore',\n        '(GMT+4) Dubai',\n        '(GMT+11) Sydney',\n        '(GMT+9) Seoul',\n      ],\n    },\n  ];\n}\n"
    },
    {
      "name": "input-group.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { lucideGlobe } from '@ng-icons/lucide';\n\nimport { ZardInputGroupAddonComponent } from '../../input-group/input-group.component';\nimport { ZardComboboxImports } from '../combobox.imports';\n\n@Component({\n  selector: 'zard-demo-combobox-input-group',\n  imports: [NgIcon, ZardComboboxImports, ZardInputGroupAddonComponent],\n  standalone: true,\n  template: `\n    <z-combobox zWidth=\"md\" [(zValue)]=\"value\">\n      <z-combobox-input placeholder=\"Select a timezone\">\n        <z-input-group-addon>\n          <ng-icon name=\"lucideGlobe\" />\n        </z-input-group-addon>\n      </z-combobox-input>\n\n      <z-combobox-content>\n        <z-combobox-empty>No timezones found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (group of timezones; track group.label) {\n            <z-combobox-group>\n              <z-combobox-label>{{ group.label }}</z-combobox-label>\n\n              @for (zone of group.options; track zone) {\n                <z-combobox-item [zValue]=\"zone\">{{ zone }}</z-combobox-item>\n              }\n            </z-combobox-group>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n  viewProviders: [provideIcons({ lucideGlobe })],\n})\nexport class ZardDemoComboboxInputGroupComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  readonly timezones = [\n    {\n      label: 'Americas',\n      options: [\n        '(GMT-5) New York',\n        '(GMT-8) Los Angeles',\n        '(GMT-6) Chicago',\n        '(GMT-5) Toronto',\n        '(GMT-8) Vancouver',\n        '(GMT-3) São Paulo',\n      ],\n    },\n    {\n      label: 'Europe',\n      options: [\n        '(GMT+0) London',\n        '(GMT+1) Paris',\n        '(GMT+1) Berlin',\n        '(GMT+1) Rome',\n        '(GMT+1) Madrid',\n        '(GMT+1) Amsterdam',\n      ],\n    },\n    {\n      label: 'Asia/Pacific',\n      options: [\n        '(GMT+9) Tokyo',\n        '(GMT+8) Shanghai',\n        '(GMT+8) Singapore',\n        '(GMT+4) Dubai',\n        '(GMT+11) Sydney',\n        '(GMT+9) Seoul',\n      ],\n    },\n  ];\n}\n"
    },
    {
      "name": "invalid.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardFieldImports } from '../../field/field.imports';\nimport { ZardComboboxImports } from '../combobox.imports';\nimport type { ZardComboboxOption } from '../combobox.types';\n\n@Component({\n  selector: 'zard-demo-combobox-invalid',\n  imports: [ZardComboboxImports, ZardFieldImports],\n  standalone: true,\n  template: `\n    <div z-field class=\"w-full min-w-48\" data-invalid=\"true\">\n      <label z-field-label for=\"combobox-invalid\">Framework</label>\n\n      <z-combobox id=\"combobox-invalid\" zInvalid [(zValue)]=\"value\">\n        <z-combobox-input placeholder=\"Select a framework\" />\n\n        <z-combobox-content>\n          <z-combobox-empty>No items found.</z-combobox-empty>\n\n          <z-combobox-list>\n            @for (framework of frameworks; track framework.value) {\n              <z-combobox-item [zValue]=\"framework.value\">{{ framework.label }}</z-combobox-item>\n            }\n          </z-combobox-list>\n        </z-combobox-content>\n      </z-combobox>\n\n      <z-field-error>Please select a framework.</z-field-error>\n    </div>\n  `,\n})\nexport class ZardDemoComboboxInvalidComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  frameworks: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React' },\n    { value: 'vue', label: 'Vue.js' },\n    { value: 'svelte', label: 'Svelte' },\n    { value: 'ember', label: 'Ember.js' },\n    { value: 'nextjs', label: 'Next.js' },\n  ];\n}\n"
    },
    {
      "name": "multiple.ts",
      "content": "import { Component, computed, signal } from '@angular/core';\n\nimport { ZardComboboxImports } from '../combobox.imports';\nimport type { ZardComboboxOption } from '../combobox.types';\n\n@Component({\n  selector: 'zard-demo-combobox-multiple',\n  imports: [ZardComboboxImports],\n  standalone: true,\n  template: `\n    <z-combobox zMultiple zAutoHighlight zWidth=\"full\" [(zValue)]=\"value\">\n      <z-combobox-chips class=\"w-full max-w-xs\">\n        @for (selected of selectedValues(); track selected) {\n          <z-combobox-chip [zValue]=\"selected\">{{ labelOf(selected) }}</z-combobox-chip>\n        }\n\n        <input z-combobox-chips-input placeholder=\"Add framework\" />\n      </z-combobox-chips>\n\n      <z-combobox-content>\n        <z-combobox-empty>No items found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (framework of frameworks; track framework.value) {\n            <z-combobox-item [zValue]=\"framework.value\">{{ framework.label }}</z-combobox-item>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n})\nexport class ZardDemoComboboxMultipleComponent {\n  readonly value = signal<string | string[] | null>(['angular']);\n\n  readonly selectedValues = computed(() => {\n    const value = this.value();\n    return Array.isArray(value) ? value : value ? [value] : [];\n  });\n\n  frameworks: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React' },\n    { value: 'vue', label: 'Vue.js' },\n    { value: 'svelte', label: 'Svelte' },\n    { value: 'ember', label: 'Ember.js' },\n    { value: 'nextjs', label: 'Next.js' },\n  ];\n\n  labelOf(value: string): string {\n    return this.frameworks.find(framework => framework.value === value)?.label ?? value;\n  }\n}\n"
    },
    {
      "name": "popup.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardButtonComponent } from '../../button/button.component';\nimport { ZardComboboxImports } from '../combobox.imports';\n\n@Component({\n  selector: 'zard-demo-combobox-popup',\n  imports: [ZardButtonComponent, ZardComboboxImports],\n  standalone: true,\n  template: `\n    <z-combobox zWidth=\"full\" class=\"w-fit\" [(zValue)]=\"value\">\n      <button type=\"button\" z-button z-combobox-trigger zType=\"outline\" class=\"w-64 justify-between font-normal\">\n        <z-combobox-value placeholder=\"Select country\" />\n      </button>\n\n      <z-combobox-content>\n        <z-combobox-input [zShowTrigger]=\"false\" placeholder=\"Search\" />\n\n        <z-combobox-empty>No items found.</z-combobox-empty>\n\n        <z-combobox-list>\n          @for (country of countries; track country.code) {\n            <z-combobox-item [zValue]=\"country.value\" [zLabel]=\"country.label\">{{ country.label }}</z-combobox-item>\n          }\n        </z-combobox-list>\n      </z-combobox-content>\n    </z-combobox>\n  `,\n})\nexport class ZardDemoComboboxPopupComponent {\n  readonly value = signal<string | string[] | null>(null);\n\n  readonly countries = [\n    { code: 'ar', value: 'argentina', label: 'Argentina', continent: 'South America' },\n    { code: 'au', value: 'australia', label: 'Australia', continent: 'Oceania' },\n    { code: 'br', value: 'brazil', label: 'Brazil', continent: 'South America' },\n    { code: 'ca', value: 'canada', label: 'Canada', continent: 'North America' },\n    { code: 'cn', value: 'china', label: 'China', continent: 'Asia' },\n    { code: 'co', value: 'colombia', label: 'Colombia', continent: 'South America' },\n    { code: 'eg', value: 'egypt', label: 'Egypt', continent: 'Africa' },\n    { code: 'fr', value: 'france', label: 'France', continent: 'Europe' },\n    { code: 'de', value: 'germany', label: 'Germany', continent: 'Europe' },\n    { code: 'it', value: 'italy', label: 'Italy', continent: 'Europe' },\n    { code: 'jp', value: 'japan', label: 'Japan', continent: 'Asia' },\n    { code: 'ke', value: 'kenya', label: 'Kenya', continent: 'Africa' },\n    { code: 'mx', value: 'mexico', label: 'Mexico', continent: 'North America' },\n    { code: 'nz', value: 'new-zealand', label: 'New Zealand', continent: 'Oceania' },\n    { code: 'ng', value: 'nigeria', label: 'Nigeria', continent: 'Africa' },\n    { code: 'za', value: 'south-africa', label: 'South Africa', continent: 'Africa' },\n    { code: 'kr', value: 'south-korea', label: 'South Korea', continent: 'Asia' },\n    { code: 'gb', value: 'united-kingdom', label: 'United Kingdom', continent: 'Europe' },\n    { code: 'us', value: 'united-states', label: 'United States', continent: 'North America' },\n  ];\n}\n"
    },
    {
      "name": "shorthand.ts",
      "content": "import { Component, signal } from '@angular/core';\n\nimport { ZardComboboxComponent } from '../combobox.component';\nimport type { ZardComboboxOption } from '../combobox.types';\n\n@Component({\n  selector: 'zard-demo-combobox-shorthand',\n  imports: [ZardComboboxComponent],\n  standalone: true,\n  template: `\n    <div class=\"flex flex-col gap-2\">\n      <z-combobox\n        [options]=\"frameworks\"\n        placeholder=\"Select framework...\"\n        searchPlaceholder=\"Search framework...\"\n        emptyText=\"No framework found.\"\n        (zComboSelected)=\"selected.set($event)\"\n      />\n\n      <p class=\"text-muted-foreground text-sm\">Selected: {{ selected()?.label ?? 'none' }}</p>\n    </div>\n  `,\n})\nexport class ZardDemoComboboxShorthandComponent {\n  readonly selected = signal<ZardComboboxOption | null>(null);\n\n  frameworks: ZardComboboxOption[] = [\n    { value: 'angular', label: 'Angular' },\n    { value: 'react', label: 'React' },\n    { value: 'vue', label: 'Vue.js' },\n    { value: 'svelte', label: 'Svelte' },\n    { value: 'ember', label: 'Ember.js' },\n    { value: 'nextjs', label: 'Next.js' },\n  ];\n}\n"
    }
  ]
}
