shadimage

Combobox

A searchable select component.

registry/brook/ui/combobox/combobox.tsx

"use client";

import { Combobox } from "@base-ui/react/combobox";
import { Check, ChevronsUpDown, X } from "lucide-react";
import type React from "react";
import { cn } from "../../../lib/utils";
import { Input } from "../input/input";
import styles from "./combobox.module.css";

function ComboboxRoot<ItemValue, Multiple extends boolean | undefined = undefined>(
  props: React.ComponentProps<typeof Combobox.Root<ItemValue, Multiple>>
) {
  return <Combobox.Root<ItemValue, Multiple> {...props} />;
}

function ComboboxTrigger({ className, children, ...props }: Combobox.Trigger.Props) {
  return (
    <Combobox.Trigger className={cn(styles.trigger, className)} data-slot="combobox-trigger" {...props}>
      {children}
      <ChevronsUpDown className={styles.icon} size={16} />
    </Combobox.Trigger>
  );
}

function ComboboxInput({ className, ...props }: Combobox.Input.Props) {
  return (
    <Combobox.Input className={cn(styles.input, className)} data-slot="combobox-input" render={<Input />} {...props} />
  );
}

function ComboboxClear({ className, children, ...props }: Combobox.Clear.Props) {
  return (
    <Combobox.Clear className={cn(styles.clear, className)} {...props}>
      {children || <X size={16} />}
    </Combobox.Clear>
  );
}

const ComboboxPortal = Combobox.Portal;

function ComboboxPositioner({ className, ...props }: Combobox.Positioner.Props) {
  return (
    <Combobox.Positioner
      className={cn(styles.positioner, className)}
      data-slot="combobox-positioner"
      sideOffset={4}
      {...props}
    />
  );
}

function ComboboxPopup({ className, children, ...props }: Combobox.Popup.Props) {
  return (
    <Combobox.Popup className={cn(styles.popup, className)} {...props}>
      <ComboboxSpacer />
      {children}
      <ComboboxSpacer />
    </Combobox.Popup>
  );
}

function ComboboxSpacer() {
  return <div style={{ height: "4px", width: "100%", flexShrink: 0 }} />;
}

function ComboboxList({ className, ...props }: Combobox.List.Props) {
  return <Combobox.List className={cn(styles.list, className)} data-slot="combobox-list" {...props} />;
}

function ComboboxEmpty({ className, children, ...props }: Combobox.Empty.Props) {
  return (
    <Combobox.Empty className={cn(styles.empty, className)} {...props}>
      {children || "No items found"}
    </Combobox.Empty>
  );
}

function ComboboxItem({
  className,
  children,
  indicatorPosition = "left",
  ...props
}: Combobox.Item.Props & {
  indicatorPosition?: "left" | "right";
}) {
  return (
    <Combobox.Item className={cn(styles.item, className)} {...props}>
      {indicatorPosition === "left" && (
        <Combobox.ItemIndicator className={styles.itemIndicator}>
          <Check size={16} />
        </Combobox.ItemIndicator>
      )}
      {children}
      {indicatorPosition === "right" && (
        <Combobox.ItemIndicator className={styles.itemIndicator}>
          <Check size={16} />
        </Combobox.ItemIndicator>
      )}
    </Combobox.Item>
  );
}

function ComboboxItemIndicator({ className, children, ...props }: Combobox.ItemIndicator.Props) {
  return (
    <Combobox.ItemIndicator
      className={cn(styles.itemIndicator, className)}
      data-slot="combobox-itemindicator"
      {...props}
    >
      {children || <Check size={16} />}
    </Combobox.ItemIndicator>
  );
}

function ComboboxGroup({ className, ...props }: Combobox.Group.Props) {
  return <Combobox.Group className={cn(styles.group, className)} {...props} />;
}

function ComboboxGroupLabel({ className, ...props }: Combobox.GroupLabel.Props) {
  return (
    <Combobox.GroupLabel className={cn(styles.groupLabel, className)} data-slot="combobox-grouplabel" {...props} />
  );
}

function ComboboxArrow({ className, ...props }: Combobox.Arrow.Props) {
  return <Combobox.Arrow className={cn(styles.arrow, className)} {...props} />;
}

function ComboboxNoItems({ className, children, ...props }: React.ComponentProps<"div">) {
  return (
    <div className={cn(styles.noItems, className)} {...props}>
      {children || "No items found"}
    </div>
  );
}

function ComboboxChips({ className, ref, ...props }: Combobox.Chips.Props & { ref?: React.Ref<HTMLDivElement> }) {
  return <Combobox.Chips className={cn(styles.chips, className)} ref={ref} {...props} />;
}

function ComboboxChip({ className, ...props }: Combobox.Chip.Props) {
  return <Combobox.Chip className={cn(styles.chip, className)} {...props} />;
}

function ComboboxChipRemove({ className, children, ...props }: Combobox.ChipRemove.Props) {
  return (
    <Combobox.ChipRemove className={cn(styles.chipRemove, className)} {...props}>
      {children || <X size={14} />}
    </Combobox.ChipRemove>
  );
}

function ComboboxChipsInput({ className, ...props }: Combobox.Input.Props) {
  return <Combobox.Input className={cn(styles.chipsInput, className)} {...props} />;
}

const ComboboxValue = Combobox.Value;

export {
  ComboboxRoot as Combobox,
  ComboboxArrow,
  ComboboxChip,
  ComboboxChipRemove,
  ComboboxChips,
  ComboboxChipsInput,
  ComboboxClear,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxGroupLabel,
  ComboboxInput,
  ComboboxItem,
  ComboboxItemIndicator,
  ComboboxList,
  ComboboxNoItems,
  ComboboxPopup,
  ComboboxPortal,
  ComboboxPositioner,
  ComboboxSpacer,
  ComboboxTrigger,
  ComboboxValue,
};
Interaction
dropdown

DependenciesBundle weight · light

ui-primitives
@base-ui/react@1.5.0
icons
lucide-react