Desktop: Settings search filters the sections (nav and body), Enter jumps to the first match
This commit is contained in:
parent
78b1b599a5
commit
c86b583830
4 changed files with 27 additions and 1 deletions
|
|
@ -79,6 +79,8 @@ export const en = {
|
|||
'tools.list': "List",
|
||||
'tools.results': "{count} matching commands",
|
||||
'tools.noHits': "No command matches. Try a store word (listing, prices, testflight...).",
|
||||
'set.search': "Search settings",
|
||||
'set.noHits': "No setting matches",
|
||||
'palette.gActions': 'Actions',
|
||||
'palette.openApp': 'Open',
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ export const tr: Record<keyof typeof en, string> = {
|
|||
'tools.list': "Liste",
|
||||
'tools.results': "{count} eşleşen komut",
|
||||
'tools.noHits': "Eşleşen komut yok. Bir mağaza kelimesi deneyin (listing, prices, testflight...).",
|
||||
'set.search': "Ayarlarda ara",
|
||||
'set.noHits': "Eşleşen ayar yok",
|
||||
'palette.gActions': 'İşlemler',
|
||||
'palette.openApp': 'Aç',
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,21 @@ export function Settings({ onTheme: _onTheme, section }: { onTheme: () => void;
|
|||
}
|
||||
} catch (e) { setError(errorText(e)); }
|
||||
};
|
||||
// search: sections whose title or text contains the words stay; the rest hide (nav and body)
|
||||
const [q, setQ] = useState('');
|
||||
const [hits, setHits] = useState<string[] | null>(null);
|
||||
useEffect(() => {
|
||||
const words = q.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
||||
const out: string[] = [];
|
||||
for (const [id, , key] of SECTIONS) {
|
||||
const el = document.getElementById(`set-${id}`);
|
||||
const text = `${t(key)} ${el?.textContent ?? ''}`.toLowerCase();
|
||||
const ok = !words.length || words.every(w => text.includes(w));
|
||||
if (el) el.hidden = !ok;
|
||||
if (ok) out.push(id);
|
||||
}
|
||||
setHits(words.length ? out : null);
|
||||
}, [q]);
|
||||
const storeOnly = d.apps.filter(a => !a.project && !a.ignored);
|
||||
const paired = storeOnly.filter(a => a.ios && a.android);
|
||||
const singles = storeOnly.filter(a => !(a.ios && a.android));
|
||||
|
|
@ -90,7 +105,10 @@ export function Settings({ onTheme: _onTheme, section }: { onTheme: () => void;
|
|||
{error && <Notice tone="bad">{error}</Notice>}
|
||||
<div className="set-layout">
|
||||
<nav className="set-nav" aria-label={t('set.sections')}>
|
||||
{SECTIONS.map(([id, icon, key]) => (
|
||||
<div className="search-input set-search"><Icon name="search" /><input className="input input-sm" type="search" value={q} onChange={e => setQ(e.target.value)} placeholder={t('set.search')} aria-label={t('set.search')}
|
||||
onKeyDown={e => { if (e.key === 'Escape') setQ(''); if (e.key === 'Enter' && hits?.[0]) jump(hits[0]); }} /></div>
|
||||
{hits && !hits.length && <p className="small muted set-nohit">{t('set.noHits')}</p>}
|
||||
{SECTIONS.filter(([id]) => !hits || hits.includes(id)).map(([id, icon, key]) => (
|
||||
<button key={id} className="nav" aria-current={active === id ? 'true' : undefined} onClick={() => jump(id)}><Icon name={icon} /><span className="grow">{t(key)}</span></button>
|
||||
))}
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -35,3 +35,7 @@
|
|||
.scr-settings .danger-card { border-color: color-mix(in srgb, var(--bad-fg, var(--accent-solid)) 35%, var(--ds-stroke)); }
|
||||
.scr-settings .danger-card .ds-sec-ic { background: color-mix(in srgb, var(--bad-fg, var(--accent-solid)) 16%, transparent); color: var(--bad-fg, var(--accent-solid)); }
|
||||
@media (max-width: 1100px) { .scr-settings .set-layout { grid-template-columns: 1fr; } }
|
||||
.scr-settings .set-search { margin-bottom: 6px; }
|
||||
.scr-settings .set-search .input { width: 100%; }
|
||||
.scr-settings .set-nohit { padding: 4px 8px; }
|
||||
.scr-settings .set-body > [hidden] { display: none; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue