feat(SearchComponents): enhance search functionality and improve UI elements

This commit is contained in:
Jaime Idolpx 2026-06-15 16:24:54 -04:00
parent 3e6298fb5a
commit b7290eeb85
4 changed files with 103 additions and 78 deletions

View File

@ -236,7 +236,13 @@ export default function SearchAssembly64({ config, setConfig, onClose }: SearchA
}
};
const handleSearch = () => { setFilterText(''); doSearch(query, categoryFilter, 0); };
const handleSearch = () => {
setFilterText('');
const trimmed = query.trim();
const q = trimmed && !trimmed.includes(':') ? `name:${trimmed}` : trimmed;
if (q !== query) setQuery(q);
doSearch(q, categoryFilter, 0);
};
const handleLoadMore = () => doSearch(query, categoryFilter, offset, true);
const applyPreset = (group: PresetGroup, value: PresetValue) => {
@ -352,25 +358,11 @@ export default function SearchAssembly64({ config, setConfig, onClose }: SearchA
<img src={`${import.meta.env.BASE_URL}assets/favicon.a64.png`} className="w-5 h-5 flex-shrink-0 object-contain" alt="" aria-hidden="true" />
<span className="text-sm font-semibold text-neutral-700">Assembly64</span>
</div>
{hasSearched && (
<button
onClick={() => setShowFilter(v => !v)}
className={`relative p-1.5 rounded-lg transition-colors ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
title="Filter & sort results"
>
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
{/* Search input + presets + categories */}
<div className="flex-shrink-0 px-4 pt-3 pb-3 border-b border-neutral-200/70">
<div className="flex gap-2 mb-2">
<div className="flex items-center gap-1.5 mb-2">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400 pointer-events-none" />
<input
@ -397,10 +389,28 @@ export default function SearchAssembly64({ config, setConfig, onClose }: SearchA
<button
onClick={handleSearch}
disabled={isSearching}
className="px-4 py-2.5 bg-blue-600 text-white rounded-xl text-sm font-medium hover:bg-blue-700 disabled:opacity-40 transition-colors"
className="p-1.5 rounded-lg hover:bg-neutral-100 disabled:opacity-40 transition-colors flex-shrink-0 text-neutral-400 hover:text-blue-600"
title="Search"
aria-label="Search"
>
{isSearching ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Search'}
{isSearching
? <Loader2 className="w-5 h-5 animate-spin text-blue-500" />
: <Search className="w-5 h-5" />}
</button>
{hasSearched && (
<button
onClick={() => setShowFilter(v => !v)}
className={`relative p-1.5 rounded-lg transition-colors flex-shrink-0 ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
title="Filter & sort results"
>
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
{presets.length > 0 && (

View File

@ -234,25 +234,11 @@ export default function SearchCSDbNG({ config, setConfig, onClose }: SearchCSDbN
<Archive className="w-5 h-5 flex-shrink-0 text-neutral-500" aria-hidden="true" />
<span className="text-sm font-semibold text-neutral-700">CSDb-ng</span>
</div>
{hasSearched && (
<button
onClick={() => setShowFilter(v => !v)}
className={`relative p-1.5 rounded-lg transition-colors ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
title="Filter & sort results"
>
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
{/* Search input */}
<div className="flex-shrink-0 px-4 pt-3 pb-3 border-b border-neutral-200/70">
<div className="flex gap-2 mb-2">
<div className="flex items-center gap-1.5 mb-2">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400 pointer-events-none" />
<input
@ -270,10 +256,28 @@ export default function SearchCSDbNG({ config, setConfig, onClose }: SearchCSDbN
<button
onClick={() => doSearch(query)}
disabled={isSearching || !query.trim()}
className="px-4 py-2.5 bg-blue-600 text-white rounded-xl text-sm font-medium hover:bg-blue-700 disabled:opacity-40 transition-colors"
className="p-1.5 rounded-lg hover:bg-neutral-100 disabled:opacity-40 transition-colors flex-shrink-0 text-neutral-400 hover:text-blue-600"
title="Search"
aria-label="Search"
>
{isSearching ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Search'}
{isSearching
? <Loader2 className="w-5 h-5 animate-spin text-blue-500" />
: <Search className="w-5 h-5" />}
</button>
{hasSearched && (
<button
onClick={() => setShowFilter(v => !v)}
className={`relative p-1.5 rounded-lg transition-colors flex-shrink-0 ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
title="Filter & sort results"
>
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
{/* Type filter chips */}

View File

@ -233,7 +233,13 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
}
};
const handleSearch = () => { setFilterText(''); doSearch(query, 0); };
const handleSearch = () => {
setFilterText('');
const trimmed = query.trim();
const q = trimmed && !trimmed.includes(':') ? `name:${trimmed}` : trimmed;
if (q !== query) setQuery(q);
doSearch(q, 0);
};
const handleLoadMore = () => doSearch(query, offset, true);
const applyPreset = (group: PresetGroup, value: PresetValue) => {
@ -355,25 +361,11 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
<img src={`${import.meta.env.BASE_URL}assets/favicon.cbm.png`} className="w-5 h-5 flex-shrink-0 object-contain" alt="" aria-hidden="true" />
<span className="text-sm font-semibold text-neutral-700">CommoServe</span>
</div>
{hasSearched && (
<button
onClick={() => setShowFilter(v => !v)}
className={`relative p-1.5 rounded-lg transition-colors ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
title="Filter & sort results"
>
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
{/* Search input + presets + categories */}
{/* Search input + presets */}
<div className="flex-shrink-0 px-4 pt-3 pb-3 border-b border-neutral-200/70">
<div className="flex gap-2 mb-2">
<div className="flex items-center gap-1.5 mb-2">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400 pointer-events-none" />
<input
@ -400,10 +392,28 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
<button
onClick={handleSearch}
disabled={isSearching}
className="px-4 py-2.5 bg-blue-600 text-white rounded-xl text-sm font-medium hover:bg-blue-700 disabled:opacity-40 transition-colors"
className="p-1.5 rounded-lg hover:bg-neutral-100 disabled:opacity-40 transition-colors flex-shrink-0 text-neutral-400 hover:text-blue-600"
title="Search"
aria-label="Search"
>
{isSearching ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Search'}
{isSearching
? <Loader2 className="w-5 h-5 animate-spin text-blue-500" />
: <Search className="w-5 h-5" />}
</button>
{hasSearched && (
<button
onClick={() => setShowFilter(v => !v)}
className={`relative p-1.5 rounded-lg transition-colors flex-shrink-0 ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`}
title="Filter & sort results"
>
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
{presets.length > 0 && (

View File

@ -348,31 +348,11 @@ export default function SearchLocal({ config, setConfig, onClose, onOpenFolder }
<img src={`${import.meta.env.BASE_URL}favicon.ico`} className="w-5 h-5 flex-shrink-0 object-contain" alt="" aria-hidden="true" />
<span className="text-sm font-semibold text-neutral-700">Local</span>
</div>
<div className="flex items-center gap-0.5">
{dbPhase === 'ready' && !busy && (
<button onClick={handleRefreshDb} className="p-1.5 rounded-lg hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600 transition-colors" title="Reload database">
<RefreshCw className="w-4 h-4" />
</button>
)}
<button onClick={() => setShowScanConfirm(true)} disabled={busy} className="p-1.5 rounded-lg hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600 disabled:opacity-40 transition-colors" title="Scan /sd and rebuild database">
<FolderSearch className="w-4 h-4" />
</button>
{hasSearched && (
<button onClick={() => setShowFilter(v => !v)} className={`relative p-1.5 rounded-lg transition-colors ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`} title="Filter & sort">
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
</div>
{/* Search input */}
<div className="flex-shrink-0 px-4 pt-3 pb-3 border-b border-neutral-100">
<div className="flex gap-2">
<div className="flex items-center gap-1.5">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400 pointer-events-none" />
<input
@ -389,12 +369,33 @@ export default function SearchLocal({ config, setConfig, onClose, onOpenFolder }
<button
onClick={handleSearch}
disabled={busy || !query.trim()}
className="px-4 py-2.5 bg-blue-600 text-white rounded-xl text-sm font-medium hover:bg-blue-700 disabled:opacity-40 transition-colors"
className="p-1.5 rounded-lg hover:bg-neutral-100 disabled:opacity-40 transition-colors flex-shrink-0 text-neutral-400 hover:text-blue-600"
title="Search"
aria-label="Search"
>
Search
{busy
? <Loader2 className="w-5 h-5 animate-spin text-blue-500" />
: <Search className="w-5 h-5" />}
</button>
{dbPhase === 'ready' && !busy && (
<button onClick={handleRefreshDb} className="p-1.5 rounded-lg hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600 transition-colors flex-shrink-0" title="Reload database">
<RefreshCw className="w-4 h-4" />
</button>
)}
<button onClick={() => setShowScanConfirm(true)} disabled={busy} className="p-1.5 rounded-lg hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600 disabled:opacity-40 transition-colors flex-shrink-0" title="Scan /sd and rebuild database">
<FolderSearch className="w-4 h-4" />
</button>
{hasSearched && (
<button onClick={() => setShowFilter(v => !v)} className={`relative p-1.5 rounded-lg transition-colors flex-shrink-0 ${showFilter ? 'bg-blue-100 text-blue-600' : 'hover:bg-neutral-100 text-neutral-400 hover:text-neutral-600'}`} title="Filter & sort">
<SlidersHorizontal className="w-4 h-4" />
{activeFilters > 0 && (
<span className="absolute -top-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-blue-600 text-white text-[9px] flex items-center justify-center font-bold">
{activeFilters}
</span>
)}
</button>
)}
</div>
</div>
{/* Filter + sort bar — same style as MediaManager */}