import type { InputGroupProps } from '@blueprintjs/core'; import { Classes, MenuItem } from '@blueprintjs/core'; import { Suggest } from '@blueprintjs/select'; import debounce from 'lodash/debounce.js'; import type { ChangeEvent, ForwardedRef } from 'react'; import { forwardRef, isValidElement, useEffect, useMemo, useRef, useState, } from 'react'; import useCombinedRefs from '../hooks/useCombinedRefs.js'; interface UseInputProps extends BaseInputProps { value?: string; ref: ForwardedRef; } interface BaseInputProps { debounceTime?: number; autoSelect?: boolean; checkValue?: (element?: number | string) => boolean; onChange?: (value: string, event?: ChangeEvent) => void; } export interface Input2Props extends Omit, BaseInputProps { getFilterValue?: (item: FilterItem) => string; filterItems?: FilterItem[]; } function useInput(props: UseInputProps) { const { value: externalValue, debounceTime, ref, autoSelect, onChange, checkValue, } = props; const [internalValue, setValue] = useState(); const value = debounceTime ? internalValue : externalValue; const localRef = useRef(null); const innerRef = useCombinedRefs([ref, localRef]); const [isDebounced, setDebouncedStatus] = useState(false); const debounceOnChange = useMemo( () => debounce((val: string, event?: ChangeEvent) => { onChange?.(val, event); setDebouncedStatus(true); }, debounceTime), [debounceTime, onChange], ); useEffect(() => { if (debounceTime) { setValue(externalValue); } }, [debounceTime, externalValue]); useEffect(() => { if (autoSelect) { innerRef?.current?.select(); } }, [autoSelect, innerRef]); function handleChange(val: string, event?: ChangeEvent) { if (!event) return; event?.stopPropagation(); event?.preventDefault(); setDebouncedStatus(false); if (!checkValue || checkValue?.(val)) { if (debounceTime) { setValue(val); debounceOnChange(val, event); } else { onChange?.(val, event); } } } return { handleChange, setValue, innerRef, debounceOnChange, isDebounced, value, }; } function getIcon(leftIcon: any) { if (isValidElement(leftIcon)) { return {leftIcon}; } return leftIcon; } function getClasses(isDebounced: boolean) { const classes = ['text-input']; if (isDebounced) { classes.push('debounce-end'); } return classes.join(' '); } function InnerInput( props: Input2Props, ref: any, ) { const { debounceTime = 0, onChange, checkValue, value: externalValue, getFilterValue = (item: FilterItem) => item, filterItems, leftIcon, autoSelect = false, fill, name, ...otherInputProps } = props; const { handleChange, setValue, isDebounced, innerRef, value } = useInput({ value: externalValue, ref, autoSelect, debounceTime, onChange, checkValue, }); function handleItemSelect(item: FilterItem, event: any) { const val = getFilterValue(item); setValue(val); event = { ...event, target: { ...event.target, name } }; onChange?.(val, event); } function renderItem(item: FilterItem, { handleClick, modifiers }: any) { const { active, matchesPredicate } = modifiers; if (!matchesPredicate) { return null; } const val = getFilterValue(item); return ( ); } function itemPredicate(query: string, item: FilterItem) { return getFilterValue(item).toLowerCase().includes(query.toLowerCase()); } const icon = getIcon(leftIcon); const classes = getClasses(isDebounced); return ( 0 && { items: filterItems, itemRenderer: renderItem, onItemSelect: handleItemSelect, inputValueRenderer: getFilterValue, itemPredicate, popoverProps: { minimal: true, placement: 'bottom' }, noResults: ( ), })} closeOnSelect resetOnQuery /> ); } const Input2 = forwardRef(InnerInput); export { Input2 };