import { useState, useRef, useEffect } from 'react'; import { api, SearchResult } from '../api'; interface Props { onSelect: (path: string) => void; } export function SearchBar({ onSelect }: Props) { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const timerRef = useRef>(); const containerRef = useRef(null); // 防抖搜索 useEffect(() => { if (!query.trim()) { setResults([]); setOpen(false); return; } clearTimeout(timerRef.current); timerRef.current = setTimeout(async () => { setLoading(true); try { const r = await api.search(query); setResults(r); setOpen(true); } catch { setResults([]); } finally { setLoading(false); } }, 300); return () => clearTimeout(timerRef.current); }, [query]); // 点击外部关闭 useEffect(() => { const handler = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setOpen(false); } }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, []); return (
🔍 setQuery(e.target.value)} placeholder="搜索文档..." onFocus={() => results.length > 0 && setOpen(true)} /> {query && ( )} {loading && ...}
{open && results.length > 0 && (
{results.map((r, i) => (
{ onSelect(r.path); setOpen(false); setQuery(''); }} >
{r.title}
{r.path}
{r.snippet}
))}
)} {open && results.length === 0 && !loading && query && (
没有匹配的文档
)}
); }