import * as React from 'react'; import { Search } from 'lucide-react'; import debounce from 'lodash/debounce'; import * as Ariakit from '@ariakit/react'; import { Spinner, Skeleton } from '@librechat/client'; import { useLocalize } from '~/hooks'; import { cn } from '~/utils'; type SearchPickerProps = { options: TOption[]; renderOptions: (option: TOption) => React.ReactElement; query: string; onQueryChange: (query: string) => void; onPick: (pickedOption: TOption) => void; placeholder?: string; inputClassName?: string; label?: string; resetValueOnHide?: boolean; isSmallScreen?: boolean; isLoading?: boolean; minQueryLengthForNoResults?: number; }; export function SearchPicker({ options, renderOptions, onPick, onQueryChange, query, label, isSmallScreen = false, placeholder, resetValueOnHide = false, isLoading = false, minQueryLengthForNoResults = 2, }: SearchPickerProps) { const localize = useLocalize(); const [_open, setOpen] = React.useState(false); const inputRef = React.useRef(null); const [localQuery, setLocalQuery] = React.useState(query); const combobox = Ariakit.useComboboxStore({ resetValueOnHide, }); React.useEffect(() => { setLocalQuery(query); }, [query]); const debouncedOnQueryChange = React.useMemo( () => debounce((value: string) => { onQueryChange(value); }, 500), [onQueryChange], ); React.useEffect(() => { return () => { debouncedOnQueryChange.cancel(); }; }, [debouncedOnQueryChange]); const onPickHandler = (option: TOption) => { setLocalQuery(''); onQueryChange(''); onPick(option); setOpen(false); if (inputRef.current) { inputRef.current.focus(); } }; return ( {label} <>
{isLoading ? ( ) : (
0 || (localQuery.trim().length >= minQueryLengthForNoResults && !isLoading) } store={combobox} unmountOnHide autoFocusOnShow={false} modal={false} className={cn( 'animate-popover z-[9999] min-w-64 overflow-hidden rounded-2xl border border-border-light bg-surface-secondary shadow-lg', '[pointer-events:auto]', // Override body's pointer-events:none when in modal )} > {(() => { if (isLoading) { return (
{Array.from({ length: 3 }).map((_, index) => (
))}
); } if (options.length > 0) { return options.map((o) => ( onPickHandler(o)} className={cn( 'flex w-full cursor-pointer items-center px-3 text-sm', 'text-text-primary hover:bg-surface-tertiary', 'data-[active-item]:bg-surface-tertiary', )} render={renderOptions(o)} > )); } if (localQuery.trim().length >= minQueryLengthForNoResults) { return (
{localize('com_ui_no_results_found')}
{localize('com_ui_try_adjusting_search')}
); } return null; })()}
); }