'use client' import type { ReactElement, ReactNode } from 'react' import { Children, isValidElement, useId, useMemo } from 'react' import { Input } from '@/components/heroui/Input' type AutocompleteKey = string | number interface AutocompleteItemProps { children?: ReactNode key?: AutocompleteKey textValue?: string } export function AutocompleteItem(_props: AutocompleteItemProps) { return null } interface AutocompleteProps { 'aria-label'?: string autoFocus?: boolean children: ReactNode | ((item: T) => ReactElement) className?: string defaultItems?: Iterable description?: ReactNode descriptionClassName?: string endContent?: ReactNode errorMessage?: ReactNode isDisabled?: boolean isInvalid?: boolean isLoading?: boolean isReadOnly?: boolean listboxProps?: object placeholder?: string popoverProps?: object radius?: 'none' | 'sm' | 'md' | 'lg' | 'full' selectedKey?: AutocompleteKey | null size?: 'sm' | 'md' | 'lg' startContent?: ReactNode type?: string validationBehavior?: 'native' | 'aria' variant?: 'flat' | 'bordered' | 'faded' | 'underlined' onBlur?: () => void onInputChange?: (value: string) => void onSelectionChange?: (key: AutocompleteKey | null) => void } function normalizeKey(key: ReactElement['key'], index: number): string { if (key == null) return String(index) const value = String(key) return value.startsWith('.$') ? value.slice(2) : value } export function Autocomplete({ children, defaultItems, selectedKey, onInputChange, onSelectionChange, ...props }: AutocompleteProps) { const listId = useId() const options = useMemo(() => { const nodes = typeof children === 'function' ? Array.from(defaultItems ?? [], (item) => children(item)) : Children.toArray(children) return Children.toArray(nodes) .filter((child): child is ReactElement => isValidElement(child)) .map((child, index) => { const id = normalizeKey(child.key, index) const label = child.props.textValue ?? (typeof child.props.children === 'string' ? child.props.children : id) return { id, label } }) }, [children, defaultItems]) const selectedLabel = options.find((option) => option.id === String(selectedKey ?? ''))?.label ?? '' return ( <> { onInputChange?.(value) const match = options.find((option) => option.label === value || option.id === value) onSelectionChange?.(match?.id ?? null) }} /> {options.map((option) => ( ) }