import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { TextInput, Layer } from '@carbon/react'; import SelectionTick from './selection-tick.component'; import styles from '../input.scss'; type TextInputProps = React.ComponentProps; interface ComboInputProps { entries: Array; error?: Error; isLoading?: boolean; name: string; fieldProps: { value: string; labelText: string; id?: string; } & Omit; handleInputChange: (newValue: string) => void; handleSelection: (newSelection: string) => void; } const ComboInput: React.FC = ({ entries, error, isLoading, name, fieldProps, handleInputChange, handleSelection, }) => { const { t } = useTranslation(); const [highlightedEntry, setHighlightedEntry] = useState(-1); const { value = '' } = fieldProps; const [showEntries, setShowEntries] = useState(false); const comboInputRef = useRef(null); const handleFocus = useCallback(() => { setShowEntries(true); setHighlightedEntry(-1); }, [setShowEntries, setHighlightedEntry]); const filteredEntries = useMemo(() => { if (!entries) { return []; } if (!value) { return entries; } return entries.filter((entry) => entry.toLowerCase().includes(value.toLowerCase())); }, [entries, value]); const handleOptionClick = useCallback( (newSelection: string, e: React.KeyboardEvent | null = null) => { e?.preventDefault(); handleSelection(newSelection); setShowEntries(false); }, [handleSelection, setShowEntries], ); const handleKeyPress = useCallback( (e: React.KeyboardEvent) => { const totalResults = filteredEntries.length ?? 0; if (e.key === 'Tab') { setShowEntries(false); setHighlightedEntry(-1); } if (e.key === 'ArrowUp') { setHighlightedEntry((prev) => Math.max(-1, prev - 1)); } else if (e.key === 'ArrowDown') { setHighlightedEntry((prev) => Math.min(totalResults - 1, prev + 1)); } else if (e.key === 'Enter') { e.preventDefault(); if (highlightedEntry > -1) { handleOptionClick(filteredEntries[highlightedEntry]); } } }, [highlightedEntry, handleOptionClick, filteredEntries, setHighlightedEntry, setShowEntries], ); useEffect(() => { const listener = (e: MouseEvent) => { if (!comboInputRef.current.contains(e.target as Node)) { setShowEntries(false); setHighlightedEntry(-1); } }; window.addEventListener('click', listener); return () => { window.removeEventListener('click', listener); }; }, []); return (
{ setHighlightedEntry(-1); handleInputChange(e.target.value); }} onFocus={handleFocus} autoComplete={'off'} onKeyDown={handleKeyPress} />
{showEntries && (
{isLoading ? (
{t('searching', 'Searching...')}
) : error ? (
{t('errorFetchingResults', 'Error fetching results')}
) : filteredEntries.length > 0 ? ( filteredEntries.map((entry, indx) => (
handleOptionClick(entry)}>
{entry} {entry === value && }
)) ) : value ? (
{t('noMatchingResults', 'No matching results')}
) : null}
)}
); }; export default ComboInput;