import { useCallback, useEffect, useMemo, useState } from 'react'; import { useToggle } from 'react-use'; import MuiBox from '@mui/material/Box'; import ClickAwayListener from '@mui/material/ClickAwayListener'; import type { AutocompleteProps, ChipTypeMap } from '@mui/material'; import type { TextFieldProps } from '@mui/material/TextField'; import { ASSETS_URL } from '../../../consts/common'; import { CustomIcon } from '../../custom-icon'; import Components from './components'; import { StyledAutocompleteLI } from './components/styles'; import { makeOptionsArray, groupBy as defaultGroupBy } from './helpers'; import { StyledAutocomplete } from './styles'; import { BaseOptionProps, GlobalSearchBaseProps } from './types'; export const GlobalSearch = < T extends BaseOptionProps, Multiple extends boolean | undefined, DisableClearable extends boolean | undefined, FreeSolo extends boolean | undefined, ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent'] >( props: GlobalSearchBaseProps & Omit< AutocompleteProps, 'renderInput' | 'options' > ) => { const { disableGrouping = false, groupedOptions, hideEndAdornment = false, open, openOnce, options, optionsBeforeScroll, endAdornmentIconSrc, onEndAdornmentClick, disableGroupTitleCasing, minInputLength, placeholder, ...otherProps } = props; const [openGroup, setOpenGroup] = useState( groupedOptions ? Object.keys(groupedOptions)[0] : null ); if (options && groupedOptions) console.warn( `"options" and "groupedOptions" props were provided. You should only provide one of them. Using "options"` ); const memorizedOptions = useMemo(() => makeOptionsArray(groupedOptions), [groupedOptions]); const handleGroupClick = (group: string) => setOpenGroup(prev => (prev === group ? null : group)); // control inputText, open and minInputLength const defaultValue = (typeof otherProps.defaultValue === 'string' && otherProps.defaultValue) || ''; const hasStringInputValue = !otherProps.multiple && typeof otherProps.value === 'string'; const [inputText, setInputText] = useState(defaultValue); useEffect(() => { if (hasStringInputValue) setInputText(otherProps.value as string); }, [hasStringInputValue, setInputText, otherProps.value]); const [openMultiple, toggleOpenMultiple] = useToggle(false); const shouldOpenListbox = useCallback( (inputText?: string) => !minInputLength || (inputText ? inputText.length >= minInputLength : false), [minInputLength] ); const toggleOpenListbox = useCallback( (open?: boolean) => { if (typeof open === 'boolean' ? open : !openMultiple) { if (!openMultiple) { toggleOpenMultiple(true); } } else { if (openMultiple) { toggleOpenMultiple(false); } } }, [openMultiple, toggleOpenMultiple] ); const handleTextChange: TextFieldProps['onChange'] = useCallback( event => { const newValue: string = (event?.target ? event.target.value : event) || ''; setInputText(newValue); toggleOpenListbox(shouldOpenListbox(newValue)); }, [setInputText, shouldOpenListbox, toggleOpenListbox] ); const handleTextFieldClick: TextFieldProps['onClick'] = event => { if (openMultiple) toggleOpenListbox(); // toggle if already opened else toggleOpenListbox(shouldOpenListbox(inputText)); }; const handleTextFieldKeyDown: TextFieldProps['onKeyDown'] = event => { if (hasStringInputValue) { if (event.key === 'Enter') { event.stopPropagation(); toggleOpenListbox(); } else { toggleOpenListbox(true); } } }; const onClickAway = () => toggleOpenListbox(false); useEffect(() => { if (openOnce) toggleOpenMultiple(true); }, [toggleOpenMultiple, openOnce]); return ( } getOptionLabel={opt => opt.label} groupBy={!disableGrouping ? defaultGroupBy : undefined} isOptionEqualToValue={(opt, val) => opt.id && val.id ? opt.id === val.id : opt.label === val.label } open={typeof open !== 'undefined' && !minInputLength ? open : openMultiple} options={options || memorizedOptions || []} renderGroup={params => ( { event.stopPropagation(); handleGroupClick(params.group); }} expanded={openGroup === params.group} optionsBeforeScroll={optionsBeforeScroll} disableTitleCasing={disableGroupTitleCasing} /> )} renderInput={params => ( )} renderOption={(liProps, option, state) => { return ( {option.Actions && ( e.stopPropagation()} className="li-actions"> {option.Actions} )} ); }} ListboxProps={{ className: 'global-search', style: !disableGrouping ? { maxHeight: 'unset' } : undefined }} {...otherProps} /> ); }; export default GlobalSearch;