import { useDisclosure, useOutsideClick, Flex, Popover, PopoverTrigger, Input, PopoverContent, Button, InputProps, HStack, } from '@chakra-ui/react' import { Variable } from 'models' import { useState, useRef, useEffect, ChangeEvent } from 'react' import { useDebouncedCallback } from 'use-debounce' import { isEmpty } from 'utils' import { VariablesButton } from './buttons/VariablesButton' type Props = { selectedItem?: string items: string[] debounceTimeout?: number withVariableButton?: boolean onValueChange?: (value: string) => void } & InputProps export const SearchableDropdown = ({ selectedItem, items, withVariableButton = false, debounceTimeout = 1000, onValueChange, ...inputProps }: Props) => { const [carretPosition, setCarretPosition] = useState(0) const { onOpen, onClose, isOpen } = useDisclosure() const [inputValue, setInputValue] = useState(selectedItem ?? '') const debounced = useDebouncedCallback( // eslint-disable-next-line @typescript-eslint/no-empty-function onValueChange ? onValueChange : () => {}, isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? debounceTimeout : 0 ) const [filteredItems, setFilteredItems] = useState([ ...items .filter((item) => item.toLowerCase().includes((selectedItem ?? '').toLowerCase()) ) .slice(0, 50), ]) const dropdownRef = useRef(null) const inputRef = useRef(null) useEffect( () => () => { debounced.flush() }, // eslint-disable-next-line react-hooks/exhaustive-deps [] ) useEffect(() => { if (filteredItems.length > 0) return setFilteredItems([ ...items .filter((item) => item.toLowerCase().includes((selectedItem ?? '').toLowerCase()) ) .slice(0, 50), ]) if (inputRef.current === document.activeElement) onOpen() // eslint-disable-next-line react-hooks/exhaustive-deps }, [items]) useOutsideClick({ ref: dropdownRef, handler: onClose, }) const onInputChange = (e: ChangeEvent) => { if (!isOpen) onOpen() setInputValue(e.target.value) debounced(e.target.value) if (e.target.value === '') { setFilteredItems([...items.slice(0, 50)]) return } setFilteredItems([ ...items .filter((item) => item.toLowerCase().includes((inputValue ?? '').toLowerCase()) ) .slice(0, 50), ]) } const handleItemClick = (item: string) => () => { setInputValue(item) debounced(item) onClose() } const handleVariableSelected = (variable?: Variable) => { if (!inputRef.current || !variable) return const cursorPosition = carretPosition const textBeforeCursorPosition = inputRef.current.value.substring( 0, cursorPosition ) const textAfterCursorPosition = inputRef.current.value.substring( cursorPosition, inputRef.current.value.length ) const newValue = textBeforeCursorPosition + `{{${variable.name}}}` + textAfterCursorPosition setInputValue(newValue) debounced(newValue) inputRef.current.focus() setTimeout(() => { if (!inputRef.current) return inputRef.current.selectionStart = inputRef.current.selectionEnd = carretPosition + `{{${variable.name}}}`.length setCarretPosition(inputRef.current.selectionStart) }, 100) } const handleKeyUp = () => { if (!inputRef.current?.selectionStart) return setCarretPosition(inputRef.current.selectionStart) } return ( {withVariableButton && ( )} {filteredItems.length > 0 && ( <> {filteredItems.map((item, idx) => { return ( ) })} )} ) }