import { useEffect, useRef, useState } from "react"; import { useNavigate } from "react-router-dom"; import { booksApi } from "../../api/books"; import { useDebounce } from "../../hooks/useDebounce"; import type { BookListItem } from "../../types/book"; import styles from "./SearchSuggestions.module.css"; interface SearchSuggestionsProps { query: string; visible: boolean; onClose: () => void; onSelectSuggestion: () => void; } export function SearchSuggestions({ query, visible, onClose, onSelectSuggestion }: SearchSuggestionsProps) { const navigate = useNavigate(); const [suggestions, setSuggestions] = useState([]); const [loading, setLoading] = useState(false); const containerRef = useRef(null); const debouncedQuery = useDebounce(query, 200); useEffect(() => { if (!debouncedQuery.trim()) { setSuggestions([]); return; } let cancelled = false; setLoading(true); void booksApi.searchBooks({ q: debouncedQuery.trim(), page_size: 5 }).then( (res) => { if (!cancelled) { setSuggestions(res.results); setLoading(false); } }, () => { if (!cancelled) { setSuggestions([]); setLoading(false); } }, ); return () => { cancelled = true; }; }, [debouncedQuery]); // Close on click outside useEffect(() => { if (!visible) return; const handler = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { onClose(); } }; // Delay attachment to avoid the click that opened us from immediately closing const timer = setTimeout(() => document.addEventListener("click", handler), 0); return () => { clearTimeout(timer); document.removeEventListener("click", handler); }; }, [visible, onClose]); // Close on Escape useEffect(() => { if (!visible) return; const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handler); return () => document.removeEventListener("keydown", handler); }, [visible, onClose]); if (!visible || !query.trim()) return null; return (
{loading && (
Searching...
)} {!loading && suggestions.length === 0 && debouncedQuery.trim() && (
No quick suggestions
)} {suggestions.map((book) => (
{ onSelectSuggestion(); navigate(`/books/${book.id}`); }} > {book.cover_image ? ( ) : ( "📖" )}
{book.title}
{book.author || "Unknown Author"}
))}
); }