{
  "title": "Combobox",
  "description": "An interactive searchable input with a filtered list of selectable items, animated floating highlight, and multi-select chip transitions.",
  "name": "motion-combobox",
  "dependencies": [
    "motion",
    "@base-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "input-group"
  ],
  "type": "registry:ui",
  "files": [
    {
      "path": "components/paceui/motion/combobox.tsx",
      "type": "registry:ui",
      "content": "import * as React from \"react\";\n\nimport { Combobox as ComboboxPrimitive } from \"@base-ui/react\";\nimport { CheckIcon, ChevronDownIcon, XIcon } from \"lucide-react\";\nimport { AnimatePresence, type Transition, motion } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from \"@/components/ui/input-group\";\n\ninterface ComboboxHighlightContextType {\n    highlightLayoutId: string;\n    activeItemId: string | null;\n    setActiveItem: (id: string) => void;\n    clearActiveItemWithDelay: () => void;\n}\n\nconst ComboboxHighlightContext = React.createContext<ComboboxHighlightContextType | null>(null);\n\nexport type ComboboxProps<ItemValue = any, Multiple extends boolean = boolean> = ComboboxPrimitive.Root.Props<\n    ItemValue,\n    Multiple\n>;\n\nfunction Combobox<ItemValue = any, Multiple extends boolean = boolean>({\n    ...props\n}: ComboboxProps<ItemValue, Multiple>) {\n    return <ComboboxPrimitive.Root data-slot=\"combobox\" {...props} />;\n}\n\nfunction ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {\n    return <ComboboxPrimitive.Value data-slot=\"combobox-value\" {...props} />;\n}\n\nexport interface ComboboxTriggerProps extends ComboboxPrimitive.Trigger.Props {}\n\nfunction ComboboxTrigger({ className, children, ...props }: ComboboxTriggerProps) {\n    return (\n        <ComboboxPrimitive.Trigger\n            data-slot=\"combobox-trigger\"\n            className={cn(\"[&_svg:not([class*='size-'])]:size-4\", className)}\n            {...props}>\n            {children}\n            <ChevronDownIcon className=\"text-muted-foreground pointer-events-none size-4 transition-transform duration-200 in-data-[popup-open]:rotate-180\" />\n        </ComboboxPrimitive.Trigger>\n    );\n}\n\nexport interface ComboboxClearProps extends ComboboxPrimitive.Clear.Props {}\n\nfunction ComboboxClear({ className, ...props }: ComboboxClearProps) {\n    return (\n        <ComboboxPrimitive.Clear\n            data-slot=\"combobox-clear\"\n            render={<InputGroupButton variant=\"ghost\" size=\"icon-xs\" />}\n            className={cn(className)}\n            {...props}>\n            <XIcon className=\"pointer-events-none\" />\n        </ComboboxPrimitive.Clear>\n    );\n}\n\nconst ComboboxInputGroup = ComboboxPrimitive.InputGroup;\n\nexport interface ComboboxInputProps extends ComboboxPrimitive.Input.Props {\n    showTrigger?: boolean;\n    showClear?: boolean;\n}\n\nfunction ComboboxInput({\n    className,\n    children,\n    disabled = false,\n    showTrigger = true,\n    showClear = false,\n    ...props\n}: ComboboxInputProps) {\n    return (\n        <ComboboxPrimitive.InputGroup className={cn(\"w-auto\", className)} render={<InputGroup />}>\n            <ComboboxPrimitive.Input render={<InputGroupInput disabled={disabled} />} {...props} />\n            <InputGroupAddon align=\"inline-end\">\n                {showTrigger && (\n                    <InputGroupButton\n                        size=\"icon-xs\"\n                        variant=\"ghost\"\n                        data-slot=\"input-group-button\"\n                        className=\"group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent\"\n                        disabled={disabled}\n                        render={<ComboboxTrigger />}\n                    />\n                )}\n                {showClear && <ComboboxClear disabled={disabled} />}\n            </InputGroupAddon>\n            {children}\n        </ComboboxPrimitive.InputGroup>\n    );\n}\n\nexport interface ComboboxContentProps\n    extends\n        ComboboxPrimitive.Popup.Props,\n        Pick<ComboboxPrimitive.Positioner.Props, \"side\" | \"align\" | \"sideOffset\" | \"alignOffset\" | \"anchor\"> {\n    transition?: Transition;\n}\n\nfunction ComboboxContent({\n    className,\n    side = \"bottom\",\n    sideOffset = 6,\n    align = \"start\",\n    alignOffset = 0,\n    anchor,\n    transition = { type: \"spring\", stiffness: 380, damping: 26, mass: 0.8 },\n    children,\n    ...props\n}: ComboboxContentProps) {\n    const parentHighlightContext = React.useContext(ComboboxHighlightContext);\n\n    const generatedLayoutId = React.useId();\n    const highlightLayoutId = parentHighlightContext?.highlightLayoutId ?? generatedLayoutId;\n\n    const [localActiveItemId, setLocalActiveItemId] = React.useState<string | null>(null);\n    const timeoutRef = React.useRef<any>(null);\n\n    const setActiveItem =\n        parentHighlightContext?.setActiveItem ??\n        React.useCallback((id: string) => {\n            if (timeoutRef.current) clearTimeout(timeoutRef.current);\n            setLocalActiveItemId(id);\n        }, []);\n\n    const clearActiveItemWithDelay =\n        parentHighlightContext?.clearActiveItemWithDelay ??\n        React.useCallback(() => {\n            if (timeoutRef.current) clearTimeout(timeoutRef.current);\n            timeoutRef.current = setTimeout(() => {\n                setLocalActiveItemId(null);\n            }, 180);\n        }, []);\n\n    const activeItemId = parentHighlightContext ? parentHighlightContext.activeItemId : localActiveItemId;\n\n    const highlightValue: ComboboxHighlightContextType = parentHighlightContext ?? {\n        highlightLayoutId,\n        activeItemId,\n        setActiveItem,\n        clearActiveItemWithDelay,\n    };\n\n    return (\n        <ComboboxPrimitive.Portal keepMounted>\n            <ComboboxPrimitive.Positioner\n                side={side}\n                sideOffset={sideOffset}\n                align={align}\n                alignOffset={alignOffset}\n                anchor={anchor}\n                className=\"isolate z-50\">\n                <ComboboxPrimitive.Popup\n                    data-slot=\"combobox-content\"\n                    data-chips={!!anchor}\n                    {...props}\n                    render={(popupProps, state) => (\n                        <AnimatePresence initial={false}>\n                            {state.open && (\n                                <motion.div\n                                    {...popupProps}\n                                    key=\"combobox-popup\"\n                                    initial={{\n                                        opacity: 0,\n                                        scale: 0.95,\n                                        y: side === \"bottom\" ? -8 : side === \"top\" ? 8 : 0,\n                                        x: side === \"right\" ? -10 : side === \"left\" ? 10 : 0,\n                                    }}\n                                    animate={{ opacity: 1, scale: 1, y: 0, x: 0 }}\n                                    exit={{\n                                        opacity: 0,\n                                        scale: 0.95,\n                                        y: side === \"bottom\" ? -6 : side === \"top\" ? 6 : 0,\n                                        x: side === \"right\" ? -8 : side === \"left\" ? 8 : 0,\n                                    }}\n                                    transition={transition}\n                                    className={cn(\n                                        \"bg-popover text-popover-foreground ring-foreground/10 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:border-input/30 group/combobox-content relative max-h-(--available-height) max-h-72 w-(--anchor-width) max-w-(--available-width) min-w-36 origin-(--transform-origin) overflow-hidden rounded-md shadow-md ring-1 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:shadow-none\",\n                                        className,\n                                    )}>\n                                    <ComboboxHighlightContext.Provider value={highlightValue}>\n                                        <div onPointerLeave={clearActiveItemWithDelay} className=\"contents\">\n                                            {children}\n                                        </div>\n                                    </ComboboxHighlightContext.Provider>\n                                </motion.div>\n                            )}\n                        </AnimatePresence>\n                    )}\n                />\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={cn(\n                \"no-scrollbar max-h-[min(calc(--spacing(72)-(--spacing(9))),calc(var(--available-height)-(--spacing(9))))] scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0\",\n                className,\n            )}\n            {...props}\n        />\n    );\n}\n\nexport interface ComboboxItemProps extends ComboboxPrimitive.Item.Props {}\n\nfunction ComboboxItem({ className, children, ...props }: ComboboxItemProps) {\n    const highlightContext = React.useContext(ComboboxHighlightContext);\n    const itemId = React.useId();\n\n    return (\n        <ComboboxPrimitive.Item\n            data-slot=\"combobox-item\"\n            {...props}\n            render={(itemProps, state) => {\n                const isCurrentActive = highlightContext?.activeItemId === itemId || state.highlighted;\n\n                return (\n                    <motion.div\n                        {...itemProps}\n                        onPointerEnter={(e: any) => {\n                            itemProps.onPointerEnter?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        onFocus={(e: any) => {\n                            itemProps.onFocus?.(e);\n                            highlightContext?.setActiveItem(itemId);\n                        }}\n                        whileTap={{ scale: 0.98 }}\n                        transition={{ type: \"spring\", stiffness: 400, damping: 25 }}\n                        className={cn(\n                            \"group/combobox-item 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-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n                            (state.highlighted || isCurrentActive) && \"text-accent-foreground\",\n                            className,\n                        )}>\n                        {isCurrentActive && highlightContext?.highlightLayoutId && (\n                            <motion.div\n                                layoutId={highlightContext.highlightLayoutId}\n                                className=\"bg-accent pointer-events-none absolute inset-0 z-0 rounded-sm\"\n                                transition={{ type: \"spring\", stiffness: 450, damping: 32 }}\n                            />\n                        )}\n                        <span className=\"relative z-10 flex w-full items-center gap-2\">{children}</span>\n                        <span\n                            className=\"pointer-events-none absolute right-2 z-10 flex size-4 items-center justify-center\"\n                            data-slot=\"combobox-item-indicator\">\n                            <ComboboxPrimitive.ItemIndicator>\n                                <motion.span\n                                    initial={{ scale: 0, opacity: 0 }}\n                                    animate={{ scale: 1, opacity: 1 }}\n                                    exit={{ scale: 0, opacity: 0 }}\n                                    transition={{ type: \"spring\", stiffness: 400, damping: 22 }}\n                                    className=\"flex items-center justify-center\">\n                                    <CheckIcon className=\"pointer-events-none size-4\" />\n                                </motion.span>\n                            </ComboboxPrimitive.ItemIndicator>\n                        </span>\n                    </motion.div>\n                );\n            }}\n        />\n    );\n}\n\nfunction ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {\n    return <ComboboxPrimitive.Group data-slot=\"combobox-group\" className={cn(className)} {...props} />;\n}\n\nfunction ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) {\n    return (\n        <ComboboxPrimitive.GroupLabel\n            data-slot=\"combobox-label\"\n            className={cn(\"text-muted-foreground px-2 py-1.5 text-xs\", className)}\n            {...props}\n        />\n    );\n}\n\nfunction ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {\n    return <ComboboxPrimitive.Collection data-slot=\"combobox-collection\" {...props} />;\n}\n\nfunction ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {\n    return (\n        <ComboboxPrimitive.Empty\n            data-slot=\"combobox-empty\"\n            className={cn(\n                \"text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex\",\n                className,\n            )}\n            {...props}\n        />\n    );\n}\n\nfunction ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) {\n    return (\n        <ComboboxPrimitive.Separator\n            data-slot=\"combobox-separator\"\n            className={cn(\"bg-border -mx-1 my-1 h-px\", className)}\n            {...props}\n        />\n    );\n}\n\nexport interface ComboboxChipsProps\n    extends React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips>, ComboboxPrimitive.Chips.Props {}\n\nfunction ComboboxChips({ className, children, ...props }: ComboboxChipsProps) {\n    return (\n        <ComboboxPrimitive.InputGroup\n            className={cn(\n                \"dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-[3px] has-aria-invalid:ring-[3px] has-data-[slot=combobox-chip]:px-1.5\",\n                className,\n            )}>\n            <ComboboxPrimitive.Chips\n                data-slot=\"combobox-chips\"\n                className=\"flex w-full flex-wrap items-center gap-1.5\"\n                {...props}>\n                {children}\n            </ComboboxPrimitive.Chips>\n        </ComboboxPrimitive.InputGroup>\n    );\n}\n\nexport interface ComboboxChipProps extends ComboboxPrimitive.Chip.Props {\n    showRemove?: boolean;\n}\n\nfunction ComboboxChip({ className, children, showRemove = true, ...props }: ComboboxChipProps) {\n    return (\n        <ComboboxPrimitive.Chip\n            data-slot=\"combobox-chip\"\n            {...props}\n            render={(chipProps) => (\n                <motion.div\n                    {...chipProps}\n                    layout\n                    initial={{ opacity: 0, scale: 0.8 }}\n                    animate={{ opacity: 1, scale: 1 }}\n                    exit={{ opacity: 0, scale: 0.8 }}\n                    transition={{ type: \"spring\", stiffness: 450, damping: 28 }}\n                    className={cn(\n                        \"bg-muted text-foreground flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm px-1.5 text-xs font-medium whitespace-nowrap has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0\",\n                        className,\n                    )}>\n                    {children}\n                    {showRemove && (\n                        <ComboboxPrimitive.ChipRemove\n                            render={<Button variant=\"ghost\" size=\"icon-xs\" />}\n                            className=\"-ml-1 opacity-50 hover:opacity-100\"\n                            data-slot=\"combobox-chip-remove\">\n                            <XIcon className=\"pointer-events-none\" />\n                        </ComboboxPrimitive.ChipRemove>\n                    )}\n                </motion.div>\n            )}\n        />\n    );\n}\n\nfunction ComboboxChipsInput({ className, ...props }: ComboboxPrimitive.Input.Props) {\n    return (\n        <ComboboxPrimitive.Input\n            data-slot=\"combobox-chip-input\"\n            className={cn(\"min-w-16 flex-1 outline-none\", className)}\n            {...props}\n        />\n    );\n}\n\nfunction useComboboxAnchor() {\n    return React.useRef<HTMLDivElement | null>(null);\n}\n\nexport {\n    Combobox,\n    ComboboxInput,\n    ComboboxInputGroup,\n    ComboboxContent,\n    ComboboxList,\n    ComboboxItem,\n    ComboboxGroup,\n    ComboboxLabel,\n    ComboboxCollection,\n    ComboboxEmpty,\n    ComboboxSeparator,\n    ComboboxChips,\n    ComboboxChip,\n    ComboboxChipsInput,\n    ComboboxTrigger,\n    ComboboxValue,\n    useComboboxAnchor,\n};\n"
    }
  ],
  "mcp": {
    "description": "An interactive combobox and autocomplete input. Provides smooth spring popup reveal, gliding active item highlight, multi-select chip transitions, and animated checkmark.",
    "tags": [
      "component",
      "combobox",
      "autocomplete",
      "paceui",
      "animation",
      "select"
    ]
  }
}