diff --git a/src/pages/SearchPage.tsx b/src/pages/SearchPage.tsx index d8fdb336..e9e135c0 100644 --- a/src/pages/SearchPage.tsx +++ b/src/pages/SearchPage.tsx @@ -17,6 +17,8 @@ import { BookmarkPlus, Check, Loader2, + ChevronDown, + Hash, } from 'lucide-react'; import { useState, useMemo, useEffect, useCallback } from 'react'; import { Link, useNavigate, useSearchParams } from 'react-router-dom'; @@ -630,18 +632,7 @@ export function SearchPage() {
Kind - +
@@ -1067,6 +1058,159 @@ function SaveDestinationRow({ ); } +type KindOption = { + value: string; + label: string; + description: string; + parentId: string; + icon: React.ComponentType<{ className?: string }> | undefined; +}; + +function KindPicker({ + value, + options, + onChange, +}: { + value: string; + options: KindOption[]; + onChange: (v: string) => void; +}) { + const [open, setOpen] = useState(false); + const [search, setSearch] = useState(''); + + const filtered = useMemo(() => { + const q = search.toLowerCase().trim(); + if (!q) return options; + return options.filter( + (o) => + o.label.toLowerCase().includes(q) || + o.description.toLowerCase().includes(q) || + o.value.includes(q), + ); + }, [options, search]); + + const selected = value === 'all' ? null : value === 'custom' ? null : options.find((o) => o.value === value); + const SelectedIcon = selected?.icon; + + const handleSelect = (v: string) => { + onChange(v); + setOpen(false); + setSearch(''); + }; + + return ( + { setOpen(o); if (!o) setSearch(''); }}> + + + + + {/* Search input */} +
+ + setSearch(e.target.value)} + autoFocus + /> + {search && ( + + )} +
+ + {/* Scrollable list */} +
+ {/* All option */} + {!search && ( + handleSelect('all')} + /> + )} + + {filtered.map((opt) => ( + handleSelect(opt.value)} + /> + ))} + + {/* Custom option */} + {(!search || 'custom'.includes(search.toLowerCase())) && ( + handleSelect('custom')} + /> + )} + + {filtered.length === 0 && search && ( +

No kinds match

+ )} +
+
+
+ ); +} + +function KindPickerItem({ + icon: Icon, + label, + active, + onClick, +}: { + icon: React.ComponentType<{ className?: string }> | null; + label: string; + active: boolean; + onClick: () => void; +}) { + return ( + + ); +} + function AuthorFilterDropdown({ onCommit }: { onCommit: (pubkey: string, label: string) => void }) { const handleSelect = useCallback((profile: SearchProfile) => { const npub = nip19.npubEncode(profile.pubkey);