mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-09-10 01:27:11 +00:00
fix(ui): keep Settings tab bar sticky and flush on mobile
Bleed cancels content padding so the bar sits at the scroll top; sticky stays on under 900px/mobile instead of scrolling away; active tab scrolls into view on click/hash. Refs #396 Thanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
This commit is contained in:
@@ -10,6 +10,9 @@
|
||||
### Added
|
||||
- **Custom download portal product label (#386):** Agent Generator advanced branding can override or hide the product type line (`download.product_*`) on the public download page without rebuilding installers.
|
||||
|
||||
### Fixed
|
||||
- **Settings tab bar sticky + mobile (#396):** Settings horizontal tabs stay flush under the top of the content scroll area (bleed cancels content padding), keep sticky on narrow/mobile instead of scrolling away, and scroll the active tab into view when switching or opening a hash link. Ships via panel update. Verify: Settings → scroll a long form → tab bar flush at top; ≤900px / mobile → tabs remain visible while scrolling; `/settings#updates` brings Updates into the horizontal track.
|
||||
|
||||
### Changed
|
||||
- _(none yet)_
|
||||
|
||||
|
||||
@@ -468,7 +468,8 @@ body.ux35-page .mobile-bottom-nav {
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
body.app-page.is-mobile-shell .settings-shell-nav,
|
||||
/* Settings: sticky outer stays; horizontal scroll on the track only */
|
||||
body.app-page.is-mobile-shell .settings-shell-nav-track,
|
||||
body.app-page.is-mobile-shell .settings-tabs,
|
||||
body.app-page.is-mobile-shell .server-mgmt-tabs {
|
||||
display: flex;
|
||||
@@ -478,10 +479,9 @@ body.ux35-page .mobile-bottom-nav {
|
||||
scrollbar-width: none;
|
||||
gap: var(--space-xs);
|
||||
padding-bottom: var(--space-xs);
|
||||
position: static;
|
||||
}
|
||||
|
||||
body.app-page.is-mobile-shell .settings-shell-nav::-webkit-scrollbar,
|
||||
body.app-page.is-mobile-shell .settings-shell-nav-track::-webkit-scrollbar,
|
||||
body.app-page.is-mobile-shell .settings-tabs::-webkit-scrollbar,
|
||||
body.app-page.is-mobile-shell .server-mgmt-tabs::-webkit-scrollbar {
|
||||
display: none;
|
||||
|
||||
@@ -3285,20 +3285,21 @@
|
||||
}
|
||||
|
||||
.settings-shell-nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
--settings-nav-bleed: 0px;
|
||||
--settings-nav-bleed-x: 0px;
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--border-primary);
|
||||
border-radius: 0;
|
||||
overflow-x: auto;
|
||||
overscroll-behavior-x: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow: visible;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
top: calc(-1 * var(--settings-nav-bleed));
|
||||
padding-top: var(--settings-nav-bleed);
|
||||
margin-top: calc(-1 * var(--settings-nav-bleed));
|
||||
margin-inline: calc(-1 * var(--settings-nav-bleed-x));
|
||||
padding-inline: var(--settings-nav-bleed-x);
|
||||
z-index: 2;
|
||||
/* Keep labels readable while scrolling long forms / branding wallpaper */
|
||||
backdrop-filter: blur(8px);
|
||||
@@ -3306,6 +3307,23 @@
|
||||
background-color: color-mix(in srgb, var(--bg-primary) 88%, transparent);
|
||||
}
|
||||
|
||||
/* Classic layout: cancel .main-content padding so sticky bar is flush */
|
||||
.app-page:not(.ux35-page) .settings-shell-nav {
|
||||
--settings-nav-bleed: var(--space-lg);
|
||||
--settings-nav-bleed-x: var(--space-lg);
|
||||
}
|
||||
|
||||
/* Horizontal scroll lives on the track — not the sticky outer (avoids sticky breakage) */
|
||||
.settings-shell-nav-track {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
overscroll-behavior-x: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.settings-shell-nav .settings-shell-tab,
|
||||
.settings-shell-nav .settings-tab {
|
||||
display: flex;
|
||||
@@ -3768,13 +3786,6 @@
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.settings-shell-nav {
|
||||
position: static;
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.settings-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-sm);
|
||||
|
||||
@@ -424,10 +424,11 @@ body.app-page.ux35-page {
|
||||
margin-bottom: var(--ux35-card-gap);
|
||||
}
|
||||
|
||||
/* Settings shell: horizontal tabs readable over branding wallpaper */
|
||||
/* Settings shell: horizontal tabs readable over branding wallpaper; bleed cancels content padding */
|
||||
.ux35-page .settings-shell-nav {
|
||||
--settings-nav-bleed: var(--ux35-content-padding-lg);
|
||||
--settings-nav-bleed-x: var(--ux35-content-padding-lg);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background-color: color-mix(in srgb, var(--bg-primary) 92%, transparent);
|
||||
}
|
||||
@@ -579,6 +580,10 @@ body.ux35-is-resizing {
|
||||
padding: var(--ux35-content-padding);
|
||||
padding-bottom: calc(var(--ux35-content-padding) + 72px);
|
||||
}
|
||||
.ux35-page .settings-shell-nav {
|
||||
--settings-nav-bleed: var(--ux35-content-padding);
|
||||
--settings-nav-bleed-x: var(--ux35-content-padding);
|
||||
}
|
||||
.ux35-sidebar {
|
||||
width: min(86vw, 280px);
|
||||
min-width: min(86vw, 280px);
|
||||
|
||||
@@ -142,6 +142,9 @@
|
||||
const target = document.getElementById('tab-' + tab.dataset.tab);
|
||||
if (target) target.classList.add('active');
|
||||
window.history.replaceState(null, '', '#' + tab.dataset.tab);
|
||||
if (typeof tab.scrollIntoView === 'function') {
|
||||
tab.scrollIntoView({ inline: 'nearest', block: 'nearest' });
|
||||
}
|
||||
applySettingsSearchFilter();
|
||||
onSettingsTabChanged(tab.dataset.tab);
|
||||
});
|
||||
@@ -150,7 +153,7 @@
|
||||
const hash = window.location.hash.replace('#', '');
|
||||
if (['general', 'branding', 'server', 'backup', 'updates', 'auth', 'advanced', 'email'].includes(hash)) {
|
||||
const tabName = hash === 'server' ? 'general' : hash;
|
||||
const tab = document.querySelector(`[data-tab="${tabName}"]`);
|
||||
const tab = document.querySelector(`.settings-shell-tab[data-tab="${tabName}"], .settings-tab[data-tab="${tabName}"]`);
|
||||
if (tab) tab.click();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</div>
|
||||
|
||||
<div class="settings-shell">
|
||||
<nav class="settings-shell-nav mobile-tabs-scroll" role="tablist" aria-label="${_('settings.title')}">
|
||||
<nav class="settings-shell-nav" role="tablist" aria-label="${_('settings.title')}">
|
||||
<div class="settings-shell-nav-track mobile-tabs-scroll">
|
||||
<button type="button" class="settings-shell-tab settings-tab active" role="tab" aria-selected="true" id="settings-tab-general" data-tab="general" aria-controls="tab-general">
|
||||
<span class="material-icons" aria-hidden="true">tune</span>
|
||||
${_('settings.tab_general')}
|
||||
@@ -40,6 +41,7 @@
|
||||
<span class="material-icons" aria-hidden="true">code</span>
|
||||
${_('settings.tab_advanced')}
|
||||
</button>` : ''}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="settings-shell-main">
|
||||
|
||||
Reference in New Issue
Block a user