{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "combobox",
  "title": "Combobox",
  "description": "A filterable select with chips, groups and async results, in a popup that resizes as you type.",
  "dependencies": [
    "@base-ui/react@^1.8.0",
    "@tabler/icons-react",
    "class-variance-authority",
    "cn"
  ],
  "registryDependencies": [
    "https://hextaui.com/r/theme.json"
  ],
  "files": [
    {
      "path": "components/ui/combobox.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Combobox as ComboboxPrimitive } from \"@base-ui/react/combobox\"\nimport { useDirection } from \"@base-ui/react/direction-provider\"\nimport {\n  IconCheck,\n  IconChevronDown,\n  IconSearch,\n  IconX,\n} from \"@tabler/icons-react\"\nimport { cva } from \"class-variance-authority\"\nimport { cn } from \"cn\"\n\ntype ClassName<State> =\n  string | ((state: State) => string | undefined) | undefined\n\nfunction mergeClassName<State>(base: string, className: ClassName<State>) {\n  return typeof className === \"function\"\n    ? (state: State) => cn(base, className(state))\n    : cn(base, className)\n}\n\nconst ComboboxFieldContext =\n  React.createContext<React.RefObject<HTMLElement | null> | null>(null)\n\nconst ComboboxContentContext = React.createContext(false)\n\nfunction mergeRefs<T>(...refs: (React.Ref<T> | undefined)[]) {\n  return (node: T | null) => {\n    for (const ref of refs) {\n      if (typeof ref === \"function\") {\n        ref(node)\n      } else if (ref) {\n        ref.current = node\n      }\n    }\n  }\n}\n\nfunction markReady(node: HTMLElement | null) {\n  if (node) {\n    requestAnimationFrame(() => {\n      node.dataset.ready = \"\"\n    })\n  }\n}\n\nfunction useFieldRef<T extends HTMLElement>(\n  ref: React.Ref<T> | undefined,\n  ...extra: React.RefCallback<T>[]\n) {\n  const fieldRef = React.useContext(ComboboxFieldContext)\n  const [first] = extra\n\n  return React.useMemo(\n    () => mergeRefs<T>(fieldRef as React.Ref<T> | null, ref, first),\n    [fieldRef, ref, first]\n  )\n}\n\nfunction Combobox<\n  Value,\n  Multiple extends boolean | undefined = false,\n  Item = Value,\n>(props: ComboboxPrimitive.Root.Props<Value, Multiple, Item>) {\n  const fieldRef = React.useRef<HTMLElement | null>(null)\n\n  return (\n    <ComboboxFieldContext.Provider value={fieldRef}>\n      <ComboboxPrimitive.Root {...props} />\n    </ComboboxFieldContext.Provider>\n  )\n}\n\nfunction ComboboxValue(props: ComboboxPrimitive.Value.Props) {\n  return <ComboboxPrimitive.Value {...props} />\n}\n\nconst comboboxFieldVariants = cva(\n  \"w-full min-w-0 rounded-md bg-transparent text-sm inset-ring-(length:--hairline) inset-ring-input transition-[color,box-shadow,border-color] outline-none focus-visible:outline-hidden dark:bg-input/30 forced-colors:border\"\n)\n\nconst comboboxIconButton =\n  \"relative inline-flex size-7 shrink-0 items-center justify-center rounded-[max(calc(var(--radius-sm)*0.5),calc(var(--radius-md)-0.3125rem))] text-muted-foreground outline-none focus-visible:outline-hidden select-none after:absolute after:-inset-1 hover:bg-muted hover:text-foreground focus-visible:ring-3 focus-visible:ring-focus-ring disabled:pointer-events-none pointer-coarse:after:-inset-2 dark:hover:bg-muted/50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n\nfunction ComboboxTrigger({\n  className,\n  children,\n  render,\n  ref,\n  ...props\n}: ComboboxPrimitive.Trigger.Props) {\n  const setRef = useFieldRef(ref)\n  const styled = render === undefined\n\n  return (\n    <ComboboxPrimitive.Trigger\n      ref={setRef}\n      data-slot=\"combobox-trigger\"\n      render={render}\n      className={mergeClassName(\n        cn(\n          \"group/combobox-trigger [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n          styled &&\n            cn(\n              comboboxFieldVariants(),\n              \"inline-flex h-9 items-center justify-between gap-2 ps-2.5 pe-2 text-start select-none focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:inset-ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:inset-ring-destructive data-placeholder:text-muted-foreground data-popup-open:inset-ring-ring dark:hover:bg-input/50 pointer-coarse:text-[1rem] [@media(hover:hover)]:hover:bg-muted/50\"\n            )\n        ),\n        className\n      )}\n      {...props}\n    >\n      {children !== undefined ? (\n        <span\n          data-slot=\"combobox-trigger-value\"\n          className=\"min-w-0 flex-1 truncate text-start\"\n        >\n          {children}\n        </span>\n      ) : null}\n      <IconChevronDown\n        aria-hidden\n        data-slot=\"combobox-trigger-icon\"\n        className=\"text-muted-foreground transition-transform duration-200 ease-spring group-data-popup-open/combobox-trigger:rotate-180 motion-reduce:transition-none\"\n      />\n    </ComboboxPrimitive.Trigger>\n  )\n}\n\nfunction ComboboxClear({\n  className,\n  children,\n  ...props\n}: ComboboxPrimitive.Clear.Props) {\n  return (\n    <ComboboxPrimitive.Clear\n      data-slot=\"combobox-clear\"\n      aria-label=\"Clear selection\"\n      className={mergeClassName(\n        cn(\n          comboboxIconButton,\n          \"transition-[color,background-color,opacity,scale] duration-150 ease-out-quint data-ending-style:opacity-0 data-starting-style:opacity-0 motion-safe:data-ending-style:scale-75 motion-safe:data-starting-style:scale-75\"\n        ),\n        className\n      )}\n      {...props}\n    >\n      {children ?? <IconX />}\n    </ComboboxPrimitive.Clear>\n  )\n}\n\ntype ComboboxInputProps = Omit<ComboboxPrimitive.Input.Props, \"className\"> & {\n  className?: string\n  showTrigger?: boolean\n  showClear?: boolean\n}\n\nfunction ComboboxInput({\n  className,\n  children,\n  disabled,\n  showTrigger,\n  showClear = false,\n  ...props\n}: ComboboxInputProps) {\n  const inContent = React.useContext(ComboboxContentContext)\n  const setFieldRef = useFieldRef<HTMLDivElement>(undefined)\n  const trigger = showTrigger ?? !inContent\n\n  return (\n    <ComboboxPrimitive.InputGroup\n      ref={inContent ? undefined : setFieldRef}\n      data-slot=\"combobox-input-group\"\n      className={cn(\n        \"group/combobox-input relative flex shrink-0 items-center\",\n        inContent\n          ? \"m-1 mb-0 h-8 rounded-[var(--combobox-item-radius)] bg-muted/60 ps-2 text-sm dark:bg-input/30\"\n          : cn(\n              comboboxFieldVariants(),\n              \"h-9 focus-within:ring-3 focus-within:ring-focus-ring focus-within:inset-ring-ring has-[[aria-invalid=true]]:inset-ring-destructive has-[[aria-invalid=true]]:focus-within:ring-destructive/20 data-invalid:inset-ring-destructive dark:has-[[aria-invalid=true]]:focus-within:ring-destructive/40 data-disabled:cursor-not-allowed data-disabled:opacity-50\"\n            ),\n        className\n      )}\n    >\n      {inContent ? (\n        <IconSearch\n          aria-hidden\n          className=\"size-4 shrink-0 text-muted-foreground\"\n        />\n      ) : null}\n      <ComboboxPrimitive.Input\n        data-slot=\"combobox-input\"\n        disabled={disabled}\n        className={cn(\n          \"h-full w-full min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted-foreground focus-visible:outline-hidden disabled:cursor-not-allowed pointer-coarse:text-[1rem]\",\n          inContent ? \"px-2\" : \"ps-2.5 pe-1\"\n        )}\n        {...props}\n      />\n      {trigger || showClear ? (\n        <div\n          data-slot=\"combobox-input-actions\"\n          className=\"me-1 grid shrink-0 place-items-center *:col-start-1 *:row-start-1\"\n        >\n          {showClear ? (\n            <ComboboxClear\n              disabled={disabled}\n              className=\"peer/combobox-clear\"\n            />\n          ) : null}\n          {trigger ? (\n            <ComboboxPrimitive.Trigger\n              data-slot=\"combobox-trigger\"\n              aria-label=\"Show options\"\n              disabled={disabled}\n              className={cn(\n                comboboxIconButton,\n                \"group/combobox-trigger transition-[color,background-color,opacity,scale] duration-150 ease-out-quint peer-data-visible/combobox-clear:pointer-events-none peer-data-visible/combobox-clear:opacity-0 motion-safe:peer-data-visible/combobox-clear:scale-75\"\n              )}\n            >\n              <IconChevronDown\n                aria-hidden\n                className=\"transition-transform duration-200 ease-spring group-data-popup-open/combobox-trigger:rotate-180 motion-reduce:transition-none\"\n              />\n            </ComboboxPrimitive.Trigger>\n          ) : null}\n        </div>\n      ) : null}\n      {children}\n    </ComboboxPrimitive.InputGroup>\n  )\n}\n\nfunction useDirectionAttribute(dir: string | undefined) {\n  const direction = useDirection()\n  const fieldRef = React.useContext(ComboboxFieldContext)\n\n  return React.useCallback(\n    (popup: HTMLElement) => {\n      if (dir !== undefined) {\n        return\n      }\n      const field = fieldRef?.current\n      const fieldDirection =\n        field && field.isConnected ? getComputedStyle(field).direction : null\n      if (fieldDirection === \"rtl\" || direction === \"rtl\") {\n        popup.setAttribute(\"dir\", \"rtl\")\n      }\n    },\n    [dir, direction, fieldRef]\n  )\n}\n\nfunction useAnimatedHeight(applyDirection: (popup: HTMLElement) => void) {\n  return React.useCallback(\n    (sizer: HTMLDivElement | null) => {\n      const popup = sizer?.parentElement\n      if (!sizer || !popup) {\n        return\n      }\n      applyDirection(popup)\n      if (typeof ResizeObserver === \"undefined\") {\n        return\n      }\n      const observer = new ResizeObserver(([entry]) => {\n        const height =\n          entry.borderBoxSize?.[0]?.blockSize ?? entry.contentRect.height\n        popup.style.height = `${height}px`\n      })\n      observer.observe(sizer)\n      return () => {\n        observer.disconnect()\n        popup.style.height = \"\"\n      }\n    },\n    [applyDirection]\n  )\n}\n\ntype ComboboxContentProps = ComboboxPrimitive.Popup.Props &\n  Pick<\n    ComboboxPrimitive.Positioner.Props,\n    \"side\" | \"align\" | \"sideOffset\" | \"alignOffset\" | \"anchor\"\n  >\n\nfunction ComboboxContent({\n  className,\n  children,\n  side = \"bottom\",\n  sideOffset = 6,\n  align = \"start\",\n  alignOffset = 0,\n  anchor,\n  dir,\n  ...props\n}: ComboboxContentProps) {\n  const applyDirection = useDirectionAttribute(dir)\n  const sizerRef = useAnimatedHeight(applyDirection)\n\n  return (\n    <ComboboxPrimitive.Portal>\n      <ComboboxPrimitive.Positioner\n        data-slot=\"combobox-positioner\"\n        side={side}\n        sideOffset={sideOffset}\n        align={align}\n        alignOffset={alignOffset}\n        anchor={anchor}\n        className=\"isolate z-50 outline-none focus-visible:outline-hidden\"\n      >\n        <ComboboxPrimitive.Popup\n          data-slot=\"combobox-content\"\n          dir={dir}\n          className={mergeClassName(\n            \"group/combobox-content relative w-(--anchor-width) max-w-(--available-width) origin-(--transform-origin) overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-(length:--hairline) ring-foreground/10 transition-[opacity,scale,height] duration-150 ease-out-quint outline-none [--combobox-item-radius:max(calc(var(--radius-sm)*0.5),calc(var(--radius-lg)-0.25rem))] focus-visible:outline-hidden has-[>[data-slot=combobox-content-sizer]>[data-slot=combobox-input-group]]:w-[max(var(--anchor-width),15rem)] data-ending-style:opacity-0 data-ending-style:duration-100 data-starting-style:opacity-0 motion-safe:data-ending-style:scale-96 motion-safe:data-starting-style:scale-96 motion-reduce:transition-opacity forced-colors:border\",\n            className\n          )}\n          {...props}\n        >\n          <ComboboxPrimitive.Arrow\n            data-slot=\"combobox-origin\"\n            className=\"pointer-events-none invisible size-0\"\n          />\n          <div\n            ref={sizerRef}\n            data-slot=\"combobox-content-sizer\"\n            className=\"flex max-h-[min(var(--available-height),24rem)] flex-col\"\n          >\n            <ComboboxContentContext.Provider value>\n              {children}\n            </ComboboxContentContext.Provider>\n          </div>\n        </ComboboxPrimitive.Popup>\n      </ComboboxPrimitive.Positioner>\n    </ComboboxPrimitive.Portal>\n  )\n}\n\nfunction ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {\n  return (\n    <ComboboxPrimitive.List\n      data-slot=\"combobox-list\"\n      className={mergeClassName(\n        \"min-h-0 scroll-py-1 overflow-y-auto overscroll-none p-1 outline-none focus-visible:outline-hidden data-empty:p-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ComboboxItem({\n  className,\n  children,\n  ...props\n}: ComboboxPrimitive.Item.Props) {\n  return (\n    <ComboboxPrimitive.Item\n      data-slot=\"combobox-item\"\n      className={mergeClassName(\n        \"group/combobox-item relative flex min-h-8 w-full cursor-default items-center gap-2 rounded-(--combobox-item-radius) py-1.5 ps-2 pe-2 text-start text-sm wrap-anywhere outline-none select-none focus-visible:outline-hidden data-highlighted:bg-accent data-highlighted:text-accent-foreground forced-colors:data-highlighted:outline-2 forced-colors:data-highlighted:-outline-offset-2 forced-colors:data-highlighted:outline-solid pointer-coarse:min-h-11 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n        className\n      )}\n      {...props}\n    >\n      {children}\n      <ComboboxPrimitive.ItemIndicator\n        keepMounted\n        data-slot=\"combobox-item-indicator\"\n        className=\"ms-auto flex size-4 shrink-0 items-center justify-center opacity-0 transition-[opacity,scale] duration-150 ease-out-quint data-[selected]:opacity-100 motion-safe:scale-50 motion-safe:data-[selected]:scale-100 motion-reduce:transition-none\"\n      >\n        <IconCheck className=\"text-foreground\" />\n      </ComboboxPrimitive.ItemIndicator>\n    </ComboboxPrimitive.Item>\n  )\n}\n\nfunction ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {\n  return (\n    <ComboboxPrimitive.Group\n      data-slot=\"combobox-group\"\n      className={mergeClassName(\"flex flex-col\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ComboboxLabel({\n  className,\n  ...props\n}: ComboboxPrimitive.GroupLabel.Props) {\n  return (\n    <ComboboxPrimitive.GroupLabel\n      data-slot=\"combobox-label\"\n      className={mergeClassName(\n        \"px-2 pt-2 pb-1 text-xs font-medium text-muted-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ComboboxCollection(props: ComboboxPrimitive.Collection.Props) {\n  return <ComboboxPrimitive.Collection {...props} />\n}\n\nfunction ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {\n  return (\n    <ComboboxPrimitive.Empty\n      data-slot=\"combobox-empty\"\n      className={mergeClassName(\n        \"shrink-0 text-center text-sm text-balance wrap-anywhere text-muted-foreground not-empty:px-3 not-empty:py-6\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ComboboxStatus({\n  className,\n  ...props\n}: ComboboxPrimitive.Status.Props) {\n  return (\n    <ComboboxPrimitive.Status\n      data-slot=\"combobox-status\"\n      className={mergeClassName(\n        \"flex shrink-0 items-center gap-2 text-sm text-muted-foreground not-empty:px-3 not-empty:py-2.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ComboboxSeparator({\n  className,\n  ...props\n}: ComboboxPrimitive.Separator.Props) {\n  return (\n    <ComboboxPrimitive.Separator\n      data-slot=\"combobox-separator\"\n      className={mergeClassName(\n        \"-mx-1 my-1 h-px shrink-0 bg-border\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\ntype ComboboxChipsProps = Omit<\n  ComboboxPrimitive.InputGroup.Props,\n  \"className\"\n> & {\n  className?: string\n}\n\nfunction ComboboxChips({\n  className,\n  children,\n  ref,\n  ...props\n}: ComboboxChipsProps) {\n  const setRef = useFieldRef<HTMLDivElement>(ref, markReady)\n\n  return (\n    <ComboboxPrimitive.InputGroup\n      ref={setRef}\n      data-slot=\"combobox-chips\"\n      className={cn(\n        comboboxFieldVariants(),\n        \"flex min-h-9 cursor-text flex-wrap items-center gap-1 p-1 focus-within:ring-3 focus-within:ring-focus-ring focus-within:inset-ring-ring has-[[aria-invalid=true]]:inset-ring-destructive has-[[aria-invalid=true]]:focus-within:ring-destructive/20 data-invalid:inset-ring-destructive dark:has-[[aria-invalid=true]]:focus-within:ring-destructive/40 data-disabled:cursor-not-allowed data-disabled:opacity-50\",\n        className\n      )}\n      {...props}\n    >\n      <ComboboxPrimitive.Chips className=\"contents\">\n        {children}\n      </ComboboxPrimitive.Chips>\n    </ComboboxPrimitive.InputGroup>\n  )\n}\n\ntype ComboboxChipProps = ComboboxPrimitive.Chip.Props & {\n  showRemove?: boolean\n}\n\nfunction ComboboxChip({\n  className,\n  children,\n  showRemove = true,\n  ...props\n}: ComboboxChipProps) {\n  return (\n    <ComboboxPrimitive.Chip\n      data-slot=\"combobox-chip\"\n      className={mergeClassName(\n        cn(\n          \"inline-flex h-6 max-w-full min-w-0 items-center gap-0.5 rounded-[max(calc(var(--radius-sm)*0.5),calc(var(--radius-md)-0.3125rem))] bg-secondary ps-2 text-xs font-medium text-secondary-foreground transition-[background-color,box-shadow] duration-150 outline-none focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden motion-safe:in-data-ready:animate-in motion-safe:in-data-ready:animation-duration-150 motion-safe:in-data-ready:fade-in-0 motion-safe:in-data-ready:zoom-in-90 data-disabled:opacity-50 [&:focus]:bg-accent [&:focus]:text-accent-foreground\",\n          showRemove ? \"pe-0.5\" : \"pe-2\"\n        ),\n        className\n      )}\n      {...props}\n    >\n      <span data-slot=\"combobox-chip-label\" className=\"min-w-0 truncate\">\n        {children}\n      </span>\n      {showRemove ? (\n        <ComboboxPrimitive.ChipRemove\n          data-slot=\"combobox-chip-remove\"\n          aria-label=\"Remove\"\n          className=\"relative inline-flex size-5 shrink-0 items-center justify-center rounded-[max(calc(var(--radius-sm)*0.5),calc(var(--radius-md)-0.4375rem))] text-muted-foreground outline-none after:absolute after:-inset-0.5 hover:bg-foreground/10 hover:text-foreground focus-visible:outline-hidden pointer-coarse:after:-inset-2 [&_svg]:pointer-events-none [&_svg]:size-3.5\"\n        >\n          <IconX />\n        </ComboboxPrimitive.ChipRemove>\n      ) : null}\n    </ComboboxPrimitive.Chip>\n  )\n}\n\nfunction ComboboxChipsInput({\n  className,\n  ...props\n}: ComboboxPrimitive.Input.Props) {\n  return (\n    <ComboboxPrimitive.Input\n      data-slot=\"combobox-chips-input\"\n      className={mergeClassName(\n        \"h-6 min-w-16 flex-1 bg-transparent px-1.5 text-sm outline-none placeholder:text-muted-foreground focus-visible:outline-hidden disabled:cursor-not-allowed pointer-coarse:text-[1rem]\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction useComboboxAnchor() {\n  return React.useRef<HTMLDivElement | null>(null)\n}\n\nconst useComboboxFilter = ComboboxPrimitive.useFilter\nconst useComboboxFilteredItems = ComboboxPrimitive.useFilteredItems\nconst createComboboxItems = ComboboxPrimitive.createItems\n\nexport {\n  Combobox,\n  ComboboxInput,\n  ComboboxContent,\n  ComboboxList,\n  ComboboxItem,\n  ComboboxGroup,\n  ComboboxLabel,\n  ComboboxCollection,\n  ComboboxEmpty,\n  ComboboxStatus,\n  ComboboxSeparator,\n  ComboboxChips,\n  ComboboxChip,\n  ComboboxChipsInput,\n  ComboboxTrigger,\n  ComboboxValue,\n  ComboboxClear,\n  comboboxFieldVariants,\n  useComboboxAnchor,\n  useComboboxFilter,\n  useComboboxFilteredItems,\n  createComboboxItems,\n}\nexport type {\n  ComboboxInputProps,\n  ComboboxContentProps,\n  ComboboxChipsProps,\n  ComboboxChipProps,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}