style(backup): match location editor tabs to the app-detail tab design

Reuse the shared .tabs-wrapper/.tab-button/.tab-panel components (same as an
app's Config/Tasks tabs) for the location editor instead of bespoke tab CSS:
emoji + label buttons, equal-width strip, accent active state. Panels toggle
via the .active class like the rest of the UI; only the panel padding is
trimmed so it nests inside the backup row.

Also drop the now-dead 'No advanced options' empty state — every type has at
least Engine + append-only in the Advanced tab.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-23 14:46:03 +01:00
parent 8e0d470549
commit 02e4f7d6ab
2 changed files with 39 additions and 65 deletions

View File

@ -723,44 +723,14 @@
border-top: 1px dashed rgba(var(--text-rgb), 0.08);
}
/* Tabbed location editor (Connection | Retention | Advanced). Splits the
formerly long single form into one panel per concern. */
.backup-loc-tabs {
display: flex;
gap: 4px;
border-bottom: 1px solid rgba(var(--text-rgb), 0.10);
margin-bottom: 16px;
/* The location editor reuses the app-detail tab design (.tabs-wrapper /
.tab-button / .tab-panel from style.css). Only trim the panel padding so it
sits inside the backup row's own padding instead of doubling it. */
.backup-location-details .tab-panel {
padding: 16px 2px 2px;
}
.backup-loc-tab {
appearance: none;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
padding: 8px 14px;
color: rgba(var(--text-rgb), 0.6);
font: inherit;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: color 0.15s ease, border-color 0.15s ease;
}
.backup-loc-tab:hover {
color: var(--text-primary);
}
.backup-loc-tab.active {
color: var(--accent);
border-bottom-color: var(--accent);
}
.backup-loc-tab-panel[hidden] {
display: none;
}
.backup-loc-tab-panel > .category-description {
.backup-location-details .tab-panel > .category-description {
margin-top: 0;
margin-bottom: 12px;
}

View File

@ -215,7 +215,7 @@ class BackupPage {
b.setAttribute('aria-selected', on ? 'true' : 'false');
});
root.querySelectorAll(`[data-tab-panel][data-loc="${tabIdx}"]`).forEach(p => {
p.toggleAttribute('hidden', p.dataset.tabPanel !== tabName);
p.classList.toggle('active', p.dataset.tabPanel === tabName);
});
return;
}
@ -678,35 +678,41 @@ class BackupPage {
yearly: l.custom_retention ? (l.keep_yearly || '') : ''
};
const tab = (id, label) => `
<button type="button" class="backup-loc-tab${id === 'connection' ? ' active' : ''}" data-action="loc-tab" data-loc="${idx}" data-tab="${id}" role="tab" aria-selected="${id === 'connection'}">${label}</button>`;
const advancedBody = groups.advanced.length
? this.renderLocFields(idx, groups.advanced, l)
: `<div class="backup-empty-state">No advanced options for this location type.</div>`;
// Reuse the app-detail tab design (.tabs-wrapper/.tab-button/.tab-panel
// from style.css) so the Locations editor matches the rest of the UI.
const tab = (id, emoji, label) => `
<button type="button" class="tab-button${id === 'connection' ? ' active' : ''}" data-action="loc-tab" data-loc="${idx}" data-tab="${id}" role="tab" aria-selected="${id === 'connection'}">
<span class="tab-emoji">${emoji}</span>
<span class="tab-name">${label}</span>
</button>`;
return `
<div class="config-category backup-location-config" data-section="location-${idx}">
<div class="backup-loc-tabs" role="tablist">
${tab('connection', 'Connection')}
${tab('retention', 'Retention')}
${tab('advanced', 'Advanced')}
</div>
<div class="backup-loc-tab-panel" data-tab-panel="connection" data-loc="${idx}">
<p class="category-description">How LibrePortal connects to this storage location.</p>
<div class="backup-location-connection-fields" id="backup-location-${idx}-connection">
${this.renderLocFields(idx, groups.connection, l)}
<div class="tabs-wrapper">
<div class="tabs-list" role="tablist">
${tab('connection', '🔗', 'Connection')}
${tab('retention', '♻️', 'Retention')}
${tab('advanced', '⚙️', 'Advanced')}
</div>
</div>
<div class="backup-loc-tab-panel" data-tab-panel="retention" data-loc="${idx}" hidden>
<p class="category-description">When to delete old backups from this location.</p>
<div id="backup-location-${idx}-retention">
${this.formRetention(`CFG_BACKUP_LOC_${idx}_`, retentionValues, true)}
</div>
</div>
<div class="backup-loc-tab-panel" data-tab-panel="advanced" data-loc="${idx}" hidden>
<p class="category-description">Overrides most locations don't need.</p>
<div id="backup-location-${idx}-advanced">
${advancedBody}
<div class="tabs-content">
<div class="tab-panel active" data-tab-panel="connection" data-loc="${idx}">
<p class="category-description">How LibrePortal connects to this storage location.</p>
<div class="backup-location-connection-fields" id="backup-location-${idx}-connection">
${this.renderLocFields(idx, groups.connection, l)}
</div>
</div>
<div class="tab-panel" data-tab-panel="retention" data-loc="${idx}">
<p class="category-description">When to delete old backups from this location.</p>
<div id="backup-location-${idx}-retention">
${this.formRetention(`CFG_BACKUP_LOC_${idx}_`, retentionValues, true)}
</div>
</div>
<div class="tab-panel" data-tab-panel="advanced" data-loc="${idx}">
<p class="category-description">Overrides most locations don't need.</p>
<div id="backup-location-${idx}-advanced">
${this.renderLocFields(idx, groups.advanced, l)}
</div>
</div>
</div>
</div>
</div>
@ -776,9 +782,7 @@ class BackupPage {
// applies to some types), so rebuild it alongside the Connection tab.
const adv = document.getElementById(`backup-location-${idx}-advanced`);
if (adv) {
adv.innerHTML = groups.advanced.length
? this.renderLocFields(idx, groups.advanced, { ...loc, type })
: `<div class="backup-empty-state">No advanced options for this location type.</div>`;
adv.innerHTML = this.renderLocFields(idx, groups.advanced, { ...loc, type });
this.tagFieldsForSave(adv);
}