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:
@@ -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 svg { width: 12px; height: 12px; }
|
||||||
.settings-search-clear:hover { color: var(--text); background: var(--bg-elev-2); }
|
.settings-search-clear:hover { color: var(--text); background: var(--bg-elev-2); }
|
||||||
.settings-search-wrap.has-query .settings-search-clear { display: inline-flex; }
|
.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. */
|
/* Hidden section + per-row + no-results banner driven by JS. */
|
||||||
.slideover-field.is-search-hidden,
|
.slideover-field.is-search-hidden,
|
||||||
.setting-row.is-search-hidden { display: none; }
|
.setting-row.is-search-hidden { display: none; }
|
||||||
|
|||||||
@@ -413,6 +413,12 @@
|
|||||||
<svg><use href="#i-x"/></svg>
|
<svg><use href="#i-x"/></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</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 & Backup</button>
|
||||||
|
</div>
|
||||||
<div class="slideover-body" id="settingsBody">
|
<div class="slideover-body" id="settingsBody">
|
||||||
<div class="slideover-field">
|
<div class="slideover-field">
|
||||||
<div class="slideover-field-label">Appearance</div>
|
<div class="slideover-field-label">Appearance</div>
|
||||||
|
|||||||
@@ -7808,6 +7808,31 @@ function closeSettings() {
|
|||||||
$('#settingsPanel').classList.remove('is-open');
|
$('#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
|
// Filter the settings panel by text. Matches against the label + the
|
||||||
// section body so e.g. "Ctrl" finds the hotkeys section via its button
|
// section body so e.g. "Ctrl" finds the hotkeys section via its button
|
||||||
// labels. Empty query = show everything. Adds a "No matches" hint when
|
// labels. Empty query = show everything. Adds a "No matches" hint when
|
||||||
@@ -7874,6 +7899,7 @@ function applySettingsSearch(rawQuery) {
|
|||||||
if (body) body.appendChild(banner);
|
if (body) body.appendChild(banner);
|
||||||
}
|
}
|
||||||
banner.classList.toggle('is-visible', q.length > 0 && totalShownRows === 0);
|
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 -------------------
|
// ---- Auto-lock with 30s warning countdown -------------------
|
||||||
@@ -8759,6 +8785,8 @@ async function init() {
|
|||||||
// Settings search box: live filter on every input. Escape clears the
|
// Settings search box: live filter on every input. Escape clears the
|
||||||
// query (without closing the panel — the existing Esc handler also
|
// query (without closing the panel — the existing Esc handler also
|
||||||
// closes settings, but only when focus isn't inside an input).
|
// 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 sInput = $('#settingsSearch');
|
||||||
const sClear = $('#settingsSearchClear');
|
const sClear = $('#settingsSearchClear');
|
||||||
if (sInput) {
|
if (sInput) {
|
||||||
|
|||||||
Reference in New Issue
Block a user