From de0320bfcdacbfcb2ac1bdfe148e848edfc9f9af Mon Sep 17 00:00:00 2001 From: kkfluous Date: Wed, 1 Apr 2026 19:18:16 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=8A=BD=E5=8F=96=20SearchSelect?= =?UTF-8?q?=20=E4=B8=BA=E5=85=AC=E5=85=B1=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/components/SearchSelect.tsx | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/components/SearchSelect.tsx diff --git a/src/components/SearchSelect.tsx b/src/components/SearchSelect.tsx new file mode 100644 index 0000000..fc59a12 --- /dev/null +++ b/src/components/SearchSelect.tsx @@ -0,0 +1,71 @@ +import { useState, useEffect, useMemo, useRef } from 'react'; +import { ChevronDown } from 'lucide-react'; + +export function SearchSelect({ value, onChange, options, placeholder, className }: { + value: string; + onChange: (v: string) => void; + options: string[]; + placeholder: string; + className?: string; +}) { + const [open, setOpen] = useState(false); + const [query, setQuery] = useState(''); + const ref = useRef(null); + + useEffect(() => { + const handler = (e: MouseEvent) => { + if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); + }; + document.addEventListener('mousedown', handler); + return () => document.removeEventListener('mousedown', handler); + }, []); + + const filtered = useMemo(() => { + if (!query) return options; + const q = query.toLowerCase(); + return options.filter((o) => o.toLowerCase().includes(q)); + }, [options, query]); + + const displayValue = value || ''; + + return ( +
+
setOpen(!open)} + > + { setQuery(e.target.value); if (!open) setOpen(true); }} + onFocus={() => { setOpen(true); setQuery(''); }} + /> + +
+ {open && ( +
+
{ onChange(''); setQuery(''); setOpen(false); }} + > + {placeholder} +
+ {filtered.map((o) => ( +
{ onChange(o); setQuery(''); setOpen(false); }} + > + {o} +
+ ))} + {filtered.length === 0 && ( +
无匹配项
+ )} +
+ )} +
+ ); +}