feat(ui): auto-collapse sidebar on settings pages; fixed full-height rail
Auto-collapse the main sidebar on pages that have a settings rail (follows the page when enabled; expanded elsewhere), with an animated slide and a profile-menu toggle (localStorage) to disable it. Rework the settings rail to position:fixed like the main sidebar: full height (3rem -> viewport bottom), left tracks the sidebar's collapsing width via --sidebar-w, content pinned to grid column 2. Fixes the sticky scroll crop and top/bottom gaps.
This commit is contained in:
parent
fc93001cb8
commit
9fbeb352a4
3 changed files with 62 additions and 13 deletions
|
|
@ -1765,15 +1765,13 @@ .server-settings-workspace > :not(.application-settings-navigation) {
|
|||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
/* Flat rail welded to the main sidebar: full height (3rem → bottom), surface
|
||||
bled left across <main>'s gutter. Negative margins cancel main's pt breathing
|
||||
and the 2.5rem gutter (lg:px-10). */
|
||||
/* Fixed like the main sidebar (3rem → bottom); left tracks its width (--sidebar-w).
|
||||
Fixed leaves the grid, so the content sibling is pinned to column 2 below. */
|
||||
.application-settings-navigation {
|
||||
position: sticky;
|
||||
position: fixed;
|
||||
top: 3rem;
|
||||
margin-top: -1.75rem;
|
||||
margin-left: -2.5rem;
|
||||
align-self: start;
|
||||
left: var(--sidebar-w, 14rem);
|
||||
width: calc(210px + 2.5rem);
|
||||
height: calc(100dvh - 3rem);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
|
|
@ -1785,6 +1783,11 @@ @media (min-width: 1280px) {
|
|||
padding: 0.5rem;
|
||||
background: var(--rail-fill);
|
||||
border-right: 1px solid var(--coollabs-hairline);
|
||||
transition: left 200ms ease;
|
||||
}
|
||||
|
||||
.application-settings-navigation ~ * {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
html:not(.dark) .application-settings-navigation {
|
||||
|
|
@ -1808,13 +1811,14 @@ @media (min-width: 1280px) {
|
|||
background: var(--coollabs-line);
|
||||
}
|
||||
|
||||
/* Embeds: keep the surface, drop the weld/full-height. */
|
||||
/* Embeds: keep the surface, drop the fixed positioning. */
|
||||
.application-settings-navigation.is-flush {
|
||||
position: static;
|
||||
inset: auto;
|
||||
width: auto;
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
margin-top: 0;
|
||||
margin-left: 0;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,22 @@ class="flex size-5 shrink-0 items-center justify-center rounded-full bg-neutral-
|
|||
<x-theme-controls variant="menu" />
|
||||
</div>
|
||||
|
||||
<button type="button" class="listbox-option w-full" @click="toggleAutoCollapse()"
|
||||
:aria-pressed="autoCollapse" title="Collapse the sidebar on pages that have a settings menu">
|
||||
<span class="flex items-center gap-2">
|
||||
<svg class="size-4 opacity-80" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" stroke-width="1.6" />
|
||||
<path d="M9 4v16" stroke="currentColor" stroke-width="1.6" />
|
||||
</svg>
|
||||
Auto-collapse sidebar
|
||||
</span>
|
||||
<svg x-show="autoCollapse" x-cloak class="size-4 shrink-0 text-black dark:text-fg" viewBox="0 0 24 24"
|
||||
fill="none" aria-hidden="true">
|
||||
<path d="M5 12l5 5L20 7" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
||||
stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="my-1 h-px bg-neutral-200 dark:bg-white/[0.07]"></div>
|
||||
|
||||
<livewire:settings-dropdown trigger="account-menu" />
|
||||
|
|
|
|||
|
|
@ -9,21 +9,50 @@
|
|||
@auth
|
||||
<div x-data="{
|
||||
open: false,
|
||||
collapsed: localStorage.getItem('sidebarCollapsed') === 'true',
|
||||
userCollapsed: localStorage.getItem('sidebarCollapsed') === 'true',
|
||||
autoCollapse: localStorage.getItem('sidebarAutoCollapse') !== 'false',
|
||||
hasSecondBar: false,
|
||||
collapsed: false,
|
||||
pageWidth: localStorage.getItem('pageWidth') || 'full',
|
||||
sidebarReady: false,
|
||||
init() {
|
||||
this.applyCollapsed(false);
|
||||
this.$nextTick(() => {
|
||||
requestAnimationFrame(() => {
|
||||
this.sidebarReady = true;
|
||||
});
|
||||
});
|
||||
},
|
||||
targetCollapsed() {
|
||||
this.hasSecondBar = !!document.querySelector('.application-settings-navigation');
|
||||
return this.autoCollapse ? this.hasSecondBar : this.userCollapsed;
|
||||
},
|
||||
applyCollapsed(animate) {
|
||||
const target = this.targetCollapsed();
|
||||
if (target === this.collapsed) return;
|
||||
if (animate) {
|
||||
// Let the new page paint at the current width, then animate the
|
||||
// slide a frame later so the auto-collapse reads as a motion.
|
||||
this.sidebarReady = true;
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => { this.collapsed = target; }));
|
||||
} else {
|
||||
this.collapsed = target;
|
||||
}
|
||||
},
|
||||
toggleSidebar() {
|
||||
this.collapsed = !this.collapsed;
|
||||
localStorage.setItem('sidebarCollapsed', this.collapsed);
|
||||
if (!this.autoCollapse) {
|
||||
this.userCollapsed = this.collapsed;
|
||||
localStorage.setItem('sidebarCollapsed', this.collapsed);
|
||||
}
|
||||
},
|
||||
toggleAutoCollapse() {
|
||||
this.autoCollapse = !this.autoCollapse;
|
||||
localStorage.setItem('sidebarAutoCollapse', this.autoCollapse);
|
||||
this.applyCollapsed(true);
|
||||
}
|
||||
}" @open-global-search.window="open = false" @page-width-changed.window="pageWidth = $event.detail" x-cloak
|
||||
}" @open-global-search.window="open = false" @page-width-changed.window="pageWidth = $event.detail" x-on:livewire:navigated.window="applyCollapsed(true)"
|
||||
:style="{ '--sidebar-w': collapsed ? '4rem' : '14rem' }" x-cloak
|
||||
class="dark:text-inherit text-black">
|
||||
<livewire:deployments-indicator />
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue