import React, { useState, useEffect, useRef } from 'react'; import { Search, X, Check } from 'lucide-react'; import { User } from '../lib/types'; import { UserAvatar } from '../components/UserAvatar'; interface UserAutocompleteProps { users: User[]; selectedUser: User | null; onSelectUser: (user: User | null) => void; placeholder?: string; disabled?: boolean; excludeUserIds?: string[]; className?: string; } export const UserAutocomplete: React.FC = ({ users, selectedUser, onSelectUser, placeholder = 'Benutzer suchen (Name, @Benutzername oder E-Mail)…', disabled = false, excludeUserIds = [], className = '', }) => { const [query, setQuery] = useState(''); const [isOpen, setIsOpen] = useState(false); const [highlightedIndex, setHighlightedIndex] = useState(-1); const containerRef = useRef(null); const inputRef = useRef(null); // Close dropdown on outside click useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setIsOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); // Filter users based on query and exclusions const filteredUsers = React.useMemo(() => { const q = query.trim().toLowerCase(); const available = users.filter((u) => !excludeUserIds.includes(u.id)); if (!q) { return available.slice(0, 15); } return available .filter((u) => { const nameMatch = (u.displayName || '').toLowerCase().includes(q); const emailMatch = (u.email || '').toLowerCase().includes(q); const userMatch = (u.username || '').toLowerCase().includes(q); return nameMatch || emailMatch || userMatch; }) .slice(0, 15); }, [users, query, excludeUserIds]); const handleSelect = (user: User) => { onSelectUser(user); setQuery(''); setIsOpen(false); setHighlightedIndex(-1); }; const handleClear = () => { onSelectUser(null); setQuery(''); setTimeout(() => inputRef.current?.focus(), 0); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (!isOpen) { if (e.key === 'ArrowDown' || e.key === 'Enter') { setIsOpen(true); } return; } if (e.key === 'ArrowDown') { e.preventDefault(); setHighlightedIndex((prev) => prev < filteredUsers.length - 1 ? prev + 1 : 0 ); } else if (e.key === 'ArrowUp') { e.preventDefault(); setHighlightedIndex((prev) => prev > 0 ? prev - 1 : filteredUsers.length - 1 ); } else if (e.key === 'Enter') { e.preventDefault(); if (highlightedIndex >= 0 && highlightedIndex < filteredUsers.length) { handleSelect(filteredUsers[highlightedIndex]); } } else if (e.key === 'Escape') { setIsOpen(false); } }; return (
{selectedUser ? (
{selectedUser.displayName || selectedUser.email} {selectedUser.username && ( @{selectedUser.username} )} {selectedUser.email && selectedUser.displayName && ( ({selectedUser.email}) )}
{!disabled && ( )}
) : (
{ setQuery(e.target.value); setIsOpen(true); setHighlightedIndex(-1); }} onFocus={() => setIsOpen(true)} onKeyDown={handleKeyDown} placeholder={placeholder} className="w-full h-8 pl-8 pr-2.5 bg-input text-main border border-border rounded-btn focus:outline-none focus:border-accent text-xs placeholder:text-muted transition-colors disabled:opacity-50" /> {isOpen && (
{filteredUsers.length === 0 ? (
Kein passender Benutzer gefunden
) : ( filteredUsers.map((u, idx) => { const isHighlighted = idx === highlightedIndex; return ( ); }) )}
)}
)}
); };