feat(settings): group settings into 4 tabs

Long settings panel → 4 tabs (Général / Sécurité / Account / Sync & Backup).
Each section (.slideover-field) is keyed by its label text to a tab via
SETTINGS_TAB_OF; applySettingsTab toggles .is-tab-hidden on the rest. No HTML
restructure (sections were already .slideover-field siblings), no dep. The
existing settings search composes: a live query suspends the tab filter so
cross-tab matches show, clearing it restores the active tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-09 23:42:22 +01:00
parent f644f8cb57
commit bec94930b4
3 changed files with 44 additions and 0 deletions
+10
View File
@@ -2183,6 +2183,16 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
.settings-search-clear svg { width: 12px; height: 12px; }
.settings-search-clear:hover { color: var(--text); background: var(--bg-elev-2); }
.settings-search-wrap.has-query .settings-search-clear { display: inline-flex; }
/* Settings tabs — group sections; JS toggles .is-tab-hidden. */
.settings-tabs { display: flex; gap: 4px; padding: 0 16px 8px; flex-wrap: wrap; }
.settings-tab {
flex: 1; min-width: 70px; padding: 6px 10px; font-size: 12px; cursor: pointer;
color: var(--text-dim); background: var(--bg-elev-2); border: 1px solid var(--border);
border-radius: 6px;
}
.settings-tab:hover { background: var(--bg-elev-3); }
.settings-tab.is-active { color: var(--accent-fg); background: var(--accent); border-color: var(--accent); }
.slideover-field.is-tab-hidden { display: none; }
/* Hidden section + per-row + no-results banner driven by JS. */
.slideover-field.is-search-hidden,
.setting-row.is-search-hidden { display: none; }
+6
View File
@@ -413,6 +413,12 @@
<svg><use href="#i-x"/></svg>
</button>
</div>
<div class="settings-tabs" id="settingsTabs">
<button class="settings-tab is-active" data-tab="general">Général</button>
<button class="settings-tab" data-tab="security">Sécurité</button>
<button class="settings-tab" data-tab="account">Account</button>
<button class="settings-tab" data-tab="sync">Sync &amp; Backup</button>
</div>
<div class="slideover-body" id="settingsBody">
<div class="slideover-field">
<div class="slideover-field-label">Appearance</div>
+28
View File
@@ -7808,6 +7808,31 @@ function closeSettings() {
$('#settingsPanel').classList.remove('is-open');
}
// Settings tabs — each section (.slideover-field, keyed by its label text)
// belongs to one tab; the active tab shows its sections, hides the rest.
const SETTINGS_TAB_OF = {
'Appearance': 'general',
'Security': 'security', 'Clipboard privacy': 'security',
'PIN unlock': 'security', 'Quick unlock': 'security', 'Recovery key': 'security',
'Account': 'account',
'Export': 'sync', 'Sync (WebDAV)': 'sync', 'Auto-backup': 'sync', 'Import': 'sync',
};
let settingsActiveTab = 'general';
function applySettingsTab(tab) {
if (tab) settingsActiveTab = tab;
$$('#settingsTabs .settings-tab').forEach(b =>
b.classList.toggle('is-active', b.dataset.tab === settingsActiveTab));
// A live search filters rows across ALL sections — suspend the tab filter
// so a match in another tab isn't hidden. Empty search → tab filter on.
const searching = !!($('#settingsSearch') && $('#settingsSearch').value.trim());
$$('#settingsBody > .slideover-field').forEach(sec => {
const lbl = sec.querySelector('.slideover-field-label');
const t = lbl ? (SETTINGS_TAB_OF[lbl.textContent.trim()] || 'general') : 'general';
sec.classList.toggle('is-tab-hidden', !searching && t !== settingsActiveTab);
});
}
// Filter the settings panel by text. Matches against the label + the
// section body so e.g. "Ctrl" finds the hotkeys section via its button
// labels. Empty query = show everything. Adds a "No matches" hint when
@@ -7874,6 +7899,7 @@ function applySettingsSearch(rawQuery) {
if (body) body.appendChild(banner);
}
banner.classList.toggle('is-visible', q.length > 0 && totalShownRows === 0);
applySettingsTab(); // re-apply tab filter (off while searching, on when cleared)
}
// ---- Auto-lock with 30s warning countdown -------------------
@@ -8759,6 +8785,8 @@ async function init() {
// Settings search box: live filter on every input. Escape clears the
// query (without closing the panel — the existing Esc handler also
// closes settings, but only when focus isn't inside an input).
$$('#settingsTabs .settings-tab').forEach(b =>
b.addEventListener('click', () => applySettingsTab(b.dataset.tab)));
const sInput = $('#settingsSearch');
const sClear = $('#settingsSearchClear');
if (sInput) {