import { useState, useEffect, forwardRef } from 'react' import { useCombobox } from 'downshift' import classnames from 'classnames' import { escapeRegExp } from 'lodash' import { bsVersion } from '@/features/utils/bootstrap-5' import OLFormControl from '@/features/ui/components/ol/ol-form-control' import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu' import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' type DownshiftInputProps = { highlightMatches?: boolean items: string[] itemsTitle?: string inputValue: string label: string setValue: (value: string) => void inputRef?: React.ForwardedRef showLabel?: boolean showSuggestedText?: boolean } & React.InputHTMLAttributes const filterItemsByInputValue = ( items: DownshiftInputProps['items'], inputValue: DownshiftInputProps['inputValue'] ) => items.filter(item => item.toLowerCase().includes(inputValue.toLowerCase())) function Downshift({ highlightMatches = false, items, itemsTitle, inputValue, placeholder, label, setValue, disabled, inputRef, showLabel = false, showSuggestedText = false, }: DownshiftInputProps) { const [inputItems, setInputItems] = useState(items) useEffect(() => { setInputItems(items) }, [items]) const { isOpen, getLabelProps, getMenuProps, getInputProps, getComboboxProps, getItemProps, highlightedIndex, openMenu, selectedItem, } = useCombobox({ inputValue, items: inputItems, initialSelectedItem: inputValue, onSelectedItemChange: ({ selectedItem }) => { setValue(selectedItem ?? '') }, onInputValueChange: ({ inputValue = '' }) => { setInputItems(filterItemsByInputValue(items, inputValue)) }, onStateChange: ({ type }) => { if (type === useCombobox.stateChangeTypes.FunctionOpenMenu) { setInputItems(filterItemsByInputValue(items, inputValue)) } }, }) const highlightMatchedCharacters = (item: string, query: string) => { if (!query || !highlightMatches) return item const regex = new RegExp(`(${escapeRegExp(query)})`, 'gi') const parts = item.split(regex) return parts.map((part, index) => regex.test(part) ? {part} : part ) } const shouldOpen = isOpen && inputItems.length return (
{/* eslint-disable-next-line jsx-a11y/label-has-for */} ) => { setValue(event.target.value) }, onFocus: () => { if (!isOpen) { openMenu() } }, ref: inputRef, })} placeholder={placeholder} disabled={disabled} />
    {showSuggestedText && inputItems.length && ( {itemsTitle}} bs5={
  • {itemsTitle}
  • } /> )} {inputItems.map((item, index) => ( // eslint-disable-next-line jsx-a11y/role-supports-aria-props
  • {highlightMatchedCharacters(item, inputValue)}
} bs5={ {highlightMatchedCharacters(item, inputValue)} } /> ))} ) } const DownshiftInput = forwardRef< HTMLInputElement, Omit >((props, ref) => ) DownshiftInput.displayName = 'DownshiftInput' export default DownshiftInput