"use client"; import * as React from "react";import { Combobox as ComboboxPrimitive } from "@base-ui/react";import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"; import { cn } from "../lib/utils";import { Button } from "./button";import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "./input-group"; const Combobox = ComboboxPrimitive.Root; function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) { return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />;} function ComboboxTrigger({ className, children, ...props }: ComboboxPrimitive.Trigger.Props) { return ( <ComboboxPrimitive.Trigger data-slot="combobox-trigger" className={cn("[&_svg:not([class*='size-'])]:size-4", className)} {...props} > {children} <ChevronDownIcon data-slot="combobox-trigger-icon" className="pointer-events-none size-4 text-muted-foreground" /> </ComboboxPrimitive.Trigger> );} function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) { return ( <ComboboxPrimitive.Clear data-slot="combobox-clear" render={<InputGroupButton variant="ghost" size="icon-xs" />} className={cn(className)} {...props} > <XIcon className="pointer-events-none" /> </ComboboxPrimitive.Clear> );} function ComboboxInput({ className, inputClassName, children, disabled = false, showTrigger = true, showClear = false, ...props}: ComboboxPrimitive.Input.Props & { inputClassName?: string; showTrigger?: boolean; showClear?: boolean;}) { return ( <InputGroup className={cn("w-auto", className)}> <ComboboxPrimitive.Input render={<InputGroupInput disabled={disabled} className={inputClassName} />} {...props} /> <InputGroupAddon align="inline-end"> {showTrigger && ( <InputGroupButton size="icon-xs" variant="ghost" asChild data-slot="input-group-button" className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent" disabled={disabled} > <ComboboxTrigger /> </InputGroupButton> )} {showClear && <ComboboxClear disabled={disabled} />} </InputGroupAddon> {children} </InputGroup> );} function ComboboxContent({ className, side = "bottom", sideOffset = 6, align = "start", alignOffset = 0, anchor, ...props}: ComboboxPrimitive.Popup.Props & Pick< ComboboxPrimitive.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "anchor" >) { return ( <ComboboxPrimitive.Portal> <ComboboxPrimitive.Positioner side={side} sideOffset={sideOffset} align={align} alignOffset={alignOffset} anchor={anchor} // `pointer-events-auto` re-enables interaction when the popup is portaled // out of a modal dialog (Radix sets `pointer-events: none` on the body // for modal dialogs/sheets; without this the portaled list can't be // scrolled or clicked). Harmless outside a dialog (auto is the default). className="isolate z-50 pointer-events-auto" > <ComboboxPrimitive.Popup data-slot="combobox-content" data-chips={!!anchor} className={cn( "group/combobox-content relative max-h-96 w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className, )} {...props} /> </ComboboxPrimitive.Positioner> </ComboboxPrimitive.Portal> );} function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) { return ( <ComboboxPrimitive.List data-slot="combobox-list" className={cn( "max-h-[min(calc(--spacing(96)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0", className, )} {...props} /> );} function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) { return ( <ComboboxPrimitive.Item data-slot="combobox-item" className={cn( "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className, )} {...props} > {children} <ComboboxPrimitive.ItemIndicator data-slot="combobox-item-indicator" render={ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" /> } > <CheckIcon className="pointer-events-none size-4 pointer-coarse:size-5" /> </ComboboxPrimitive.ItemIndicator> </ComboboxPrimitive.Item> );} function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) { return ( <ComboboxPrimitive.Group data-slot="combobox-group" className={cn(className)} {...props} /> );} function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) { return ( <ComboboxPrimitive.GroupLabel data-slot="combobox-label" className={cn( "px-2 py-1.5 text-xs text-muted-foreground pointer-coarse:px-3 pointer-coarse:py-2 pointer-coarse:text-sm", className, )} {...props} /> );} function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) { return <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />;} function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) { return ( <ComboboxPrimitive.Empty data-slot="combobox-empty" className={cn( "hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex", className, )} {...props} /> );} function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) { return ( <ComboboxPrimitive.Separator data-slot="combobox-separator" className={cn("-mx-1 my-1 h-px bg-border", className)} {...props} /> );} function ComboboxChips({ className, ...props}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> & ComboboxPrimitive.Chips.Props) { return ( <ComboboxPrimitive.Chips data-slot="combobox-chips" className={cn( "flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-[3px] has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1.5 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40", className, )} {...props} /> );} function ComboboxChip({ className, children, showRemove = true, ...props}: ComboboxPrimitive.Chip.Props & { showRemove?: boolean;}) { return ( <ComboboxPrimitive.Chip data-slot="combobox-chip" className={cn( "flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0", className, )} {...props} > {children} {showRemove && ( <ComboboxPrimitive.ChipRemove render={<Button variant="ghost" size="icon-xs" />} className="-ml-1 opacity-50 hover:opacity-100" data-slot="combobox-chip-remove" > <XIcon className="pointer-events-none" /> </ComboboxPrimitive.ChipRemove> )} </ComboboxPrimitive.Chip> );} function ComboboxChipsInput({ className, ...props }: ComboboxPrimitive.Input.Props) { return ( <ComboboxPrimitive.Input data-slot="combobox-chip-input" className={cn("min-w-16 flex-1 outline-none", className)} {...props} /> );} function useComboboxAnchor() { return React.useRef<HTMLDivElement | null>(null);} export interface FreeformComboboxOption { readonly value: string; readonly label?: React.ReactNode; readonly description?: React.ReactNode;} /** How many options the popup renders at once. base-ui slices the filtered list * to this, so a multi-thousand-operation spec stays cheap: the empty-query open * shows the top slice, and typing narrows to the top matches. */const FREEFORM_COMBOBOX_LIMIT = 100; function FreeformCombobox(props: { readonly value: string; readonly onValueChange: (value: string) => void; readonly options: readonly FreeformComboboxOption[]; readonly placeholder?: string; readonly emptyLabel?: React.ReactNode; readonly className?: string; readonly inputClassName?: string; readonly disabled?: boolean; /** Forwarded to the underlying text input so a `<Label htmlFor>` can target it * (and tests get a stable selector). */ readonly id?: string;}) { const valueOption = props.value.trim(); const propsOptions = props.options; const options = React.useMemo<readonly FreeformComboboxOption[]>( () => valueOption.length > 0 && !propsOptions.some((option) => option.value === valueOption) ? [{ value: valueOption, label: valueOption }, ...propsOptions] : propsOptions, [valueOption, propsOptions], ); const selectedValue = options.some((option) => option.value === props.value) ? props.value : null; const byValue = React.useMemo(() => { const map = new Map<string, FreeformComboboxOption>(); for (const option of options) map.set(option.value, option); return map; }, [options]); const items = React.useMemo(() => options.map((option) => option.value), [options]); // base-ui only filters the popup when the list renders from `items` (the // function-child / closed-template path below); a statically-mapped list never // narrows as you type. Match the query against the value AND its string // label/description, so typing a summary word or the human label finds the // option, not just its raw id. const filter = React.useCallback( (item: string, query: string) => { const needle = query.trim().toLowerCase(); if (needle === "") return true; const option = byValue.get(item); const haystacks = [ item, typeof option?.label === "string" ? option.label : "", typeof option?.description === "string" ? option.description : "", ]; return haystacks.some((part) => part.toLowerCase().includes(needle)); }, [byValue], ); return ( <Combobox items={items} inputValue={props.value} value={selectedValue} filter={filter} limit={FREEFORM_COMBOBOX_LIMIT} onInputValueChange={props.onValueChange} onValueChange={(value) => { if (value !== null) props.onValueChange(value); }} > <ComboboxInput id={props.id} placeholder={props.placeholder} className={props.className} inputClassName={props.inputClassName} disabled={props.disabled} showClear={props.value.length > 0} /> <ComboboxContent> <ComboboxEmpty>{props.emptyLabel ?? "No options"}</ComboboxEmpty> <ComboboxList> {(item: string) => { const option = byValue.get(item); return ( <ComboboxItem key={item} value={item}> <div className="min-w-0 flex-1"> <div className="truncate">{option?.label ?? item}</div> {option?.description && ( <div className="mt-0.5 truncate text-[10px] text-muted-foreground"> {option.description} </div> )} </div> </ComboboxItem> ); }} </ComboboxList> </ComboboxContent> </Combobox> );} export { Combobox, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, ComboboxChips, ComboboxChip, ComboboxChipsInput, ComboboxTrigger, ComboboxValue, FreeformCombobox, useComboboxAnchor,};
"use client";
import * as React from "react";
import { Combobox as ComboboxPrimitive } from "@base-ui/react";
import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react";
import { cn } from "../lib/utils";
import { Button } from "./button";
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from "./input-group";
const Combobox = ComboboxPrimitive.Root;
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />;
}
function ComboboxTrigger({ className, children, ...props }: ComboboxPrimitive.Trigger.Props) {
return (
<ComboboxPrimitive.Trigger
data-slot="combobox-trigger"
className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
{...props}
>
{children}
<ChevronDownIcon
data-slot="combobox-trigger-icon"
className="pointer-events-none size-4 text-muted-foreground"
/>
</ComboboxPrimitive.Trigger>
);
}
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
return (
<ComboboxPrimitive.Clear
data-slot="combobox-clear"
render={<InputGroupButton variant="ghost" size="icon-xs" />}
className={cn(className)}
{...props}
>
<XIcon className="pointer-events-none" />
</ComboboxPrimitive.Clear>
);
}
function ComboboxInput({
className,
inputClassName,
children,
disabled = false,
showTrigger = true,
showClear = false,
...props
}: ComboboxPrimitive.Input.Props & {
inputClassName?: string;
showTrigger?: boolean;
showClear?: boolean;
}) {
return (
<InputGroup className={cn("w-auto", className)}>
<ComboboxPrimitive.Input
render={<InputGroupInput disabled={disabled} className={inputClassName} />}
{...props}
/>
<InputGroupAddon align="inline-end">
{showTrigger && (
<InputGroupButton
size="icon-xs"
variant="ghost"
asChild
data-slot="input-group-button"
className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
disabled={disabled}
>
<ComboboxTrigger />
</InputGroupButton>
)}
{showClear && <ComboboxClear disabled={disabled} />}
</InputGroupAddon>
{children}
</InputGroup>
);
}
function ComboboxContent({
className,
side = "bottom",
sideOffset = 6,
align = "start",
alignOffset = 0,
anchor,
...props
}: ComboboxPrimitive.Popup.Props &
Pick<
ComboboxPrimitive.Positioner.Props,
"side" | "align" | "sideOffset" | "alignOffset" | "anchor"
>) {
return (
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Positioner
side={side}
sideOffset={sideOffset}
align={align}
alignOffset={alignOffset}
anchor={anchor}
// `pointer-events-auto` re-enables interaction when the popup is portaled
// out of a modal dialog (Radix sets `pointer-events: none` on the body
// for modal dialogs/sheets; without this the portaled list can't be
// scrolled or clicked). Harmless outside a dialog (auto is the default).
className="isolate z-50 pointer-events-auto"
>
<ComboboxPrimitive.Popup
data-slot="combobox-content"
data-chips={!!anchor}
className={cn(
"group/combobox-content relative max-h-96 w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
{...props}
/>
</ComboboxPrimitive.Positioner>
</ComboboxPrimitive.Portal>
);
}
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
return (
<ComboboxPrimitive.List
data-slot="combobox-list"
className={cn(
"max-h-[min(calc(--spacing(96)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0",
className,
)}
{...props}
/>
);
}
function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props) {
return (
<ComboboxPrimitive.Item
data-slot="combobox-item"
className={cn(
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
{children}
<ComboboxPrimitive.ItemIndicator
data-slot="combobox-item-indicator"
render={
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
}
>
<CheckIcon className="pointer-events-none size-4 pointer-coarse:size-5" />
</ComboboxPrimitive.ItemIndicator>
</ComboboxPrimitive.Item>
);
}
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
return (
<ComboboxPrimitive.Group data-slot="combobox-group" className={cn(className)} {...props} />
);
}
function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) {
return (
<ComboboxPrimitive.GroupLabel
data-slot="combobox-label"
className={cn(
"px-2 py-1.5 text-xs text-muted-foreground pointer-coarse:px-3 pointer-coarse:py-2 pointer-coarse:text-sm",
className,
)}
{...props}
/>
);
}
function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
return <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />;
}
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
return (
<ComboboxPrimitive.Empty
data-slot="combobox-empty"
className={cn(
"hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex",
className,
)}
{...props}
/>
);
}
function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) {
return (
<ComboboxPrimitive.Separator
data-slot="combobox-separator"
className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props}
/>
);
}
function ComboboxChips({
className,
...props
}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> & ComboboxPrimitive.Chips.Props) {
return (
<ComboboxPrimitive.Chips
data-slot="combobox-chips"
className={cn(
"flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-[3px] has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1.5 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40",
className,
)}
{...props}
/>
);
}
function ComboboxChip({
className,
children,
showRemove = true,
...props
}: ComboboxPrimitive.Chip.Props & {
showRemove?: boolean;
}) {
return (
<ComboboxPrimitive.Chip
data-slot="combobox-chip"
className={cn(
"flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0",
className,
)}
{...props}
>
{children}
{showRemove && (
<ComboboxPrimitive.ChipRemove
render={<Button variant="ghost" size="icon-xs" />}
className="-ml-1 opacity-50 hover:opacity-100"
data-slot="combobox-chip-remove"
>
<XIcon className="pointer-events-none" />
</ComboboxPrimitive.ChipRemove>
)}
</ComboboxPrimitive.Chip>
);
}
function ComboboxChipsInput({ className, ...props }: ComboboxPrimitive.Input.Props) {
return (
<ComboboxPrimitive.Input
data-slot="combobox-chip-input"
className={cn("min-w-16 flex-1 outline-none", className)}
{...props}
/>
);
}
function useComboboxAnchor() {
return React.useRef<HTMLDivElement | null>(null);
}
export interface FreeformComboboxOption {
readonly value: string;
readonly label?: React.ReactNode;
readonly description?: React.ReactNode;
}
/** How many options the popup renders at once. base-ui slices the filtered list
* to this, so a multi-thousand-operation spec stays cheap: the empty-query open
* shows the top slice, and typing narrows to the top matches. */
const FREEFORM_COMBOBOX_LIMIT = 100;
function FreeformCombobox(props: {
readonly value: string;
readonly onValueChange: (value: string) => void;
readonly options: readonly FreeformComboboxOption[];
readonly placeholder?: string;
readonly emptyLabel?: React.ReactNode;
readonly className?: string;
readonly inputClassName?: string;
readonly disabled?: boolean;
/** Forwarded to the underlying text input so a `<Label htmlFor>` can target it
* (and tests get a stable selector). */
readonly id?: string;
}) {
const valueOption = props.value.trim();
const propsOptions = props.options;
const options = React.useMemo<readonly FreeformComboboxOption[]>(
() =>
valueOption.length > 0 && !propsOptions.some((option) => option.value === valueOption)
? [{ value: valueOption, label: valueOption }, ...propsOptions]
: propsOptions,
[valueOption, propsOptions],
);
const selectedValue = options.some((option) => option.value === props.value) ? props.value : null;
const byValue = React.useMemo(() => {
const map = new Map<string, FreeformComboboxOption>();
for (const option of options) map.set(option.value, option);
return map;
}, [options]);
const items = React.useMemo(() => options.map((option) => option.value), [options]);
// base-ui only filters the popup when the list renders from `items` (the
// function-child / closed-template path below); a statically-mapped list never
// narrows as you type. Match the query against the value AND its string
// label/description, so typing a summary word or the human label finds the
// option, not just its raw id.
const filter = React.useCallback(
(item: string, query: string) => {
const needle = query.trim().toLowerCase();
if (needle === "") return true;
const option = byValue.get(item);
const haystacks = [
item,
typeof option?.label === "string" ? option.label : "",
typeof option?.description === "string" ? option.description : "",
];
return haystacks.some((part) => part.toLowerCase().includes(needle));
},
[byValue],
);
return (
<Combobox
items={items}
inputValue={props.value}
value={selectedValue}
filter={filter}
limit={FREEFORM_COMBOBOX_LIMIT}
onInputValueChange={props.onValueChange}
onValueChange={(value) => {
if (value !== null) props.onValueChange(value);
}}
>
<ComboboxInput
id={props.id}
placeholder={props.placeholder}
className={props.className}
inputClassName={props.inputClassName}
disabled={props.disabled}
showClear={props.value.length > 0}
/>
<ComboboxContent>
<ComboboxEmpty>{props.emptyLabel ?? "No options"}</ComboboxEmpty>
<ComboboxList>
{(item: string) => {
const option = byValue.get(item);
return (
<ComboboxItem key={item} value={item}>
<div className="min-w-0 flex-1">
<div className="truncate">{option?.label ?? item}</div>
{option?.description && (
<div className="mt-0.5 truncate text-[10px] text-muted-foreground">
{option.description}
</div>
)}
</div>
</ComboboxItem>
);
}}
</ComboboxList>
</ComboboxContent>
</Combobox>
);
}
export {
Combobox,
ComboboxInput,
ComboboxContent,
ComboboxList,
ComboboxItem,
ComboboxGroup,
ComboboxLabel,
ComboboxCollection,
ComboboxEmpty,
ComboboxSeparator,
ComboboxChips,
ComboboxChip,
ComboboxChipsInput,
ComboboxTrigger,
ComboboxValue,
FreeformCombobox,
useComboboxAnchor,
};