Browse Source

fix(ui): Reconcile sidebar state when crossing the responsive breakpoint

SideNav classified the viewport only during construction. Resizing past
the `lg` breakpoint therefore left the desktop sidebar in its previous
mobile state, and the menu group containing the current page remained
collapsed until the pointer entered the sidebar.

Use the existing desktop media query as an event source. Apply the
persisted pin preference directly to the body attributes during
initialization and on each breakpoint transition, then reinitialize the
active menu section.

Avoid calling `pin()` or `unpin()` during responsive transitions so
Bootstrap retains ownership of its collapse state and the stored
preference is not rewritten unnecessarily.

Also remove the `show` class from a menu group toggle when collapsing
its section to keep the toggle and menu state synchronized.

Fixes #23035
Martin Hauser 17 hours ago
parent
commit
fe194e7003

File diff suppressed because it is too large
+ 0 - 0
netbox/project-static/dist/netbox.js


File diff suppressed because it is too large
+ 0 - 0
netbox/project-static/dist/netbox.js.map


+ 24 - 10
netbox/project-static/src/sidenav.ts

@@ -66,22 +66,34 @@ class SideNav {
       toggler.addEventListener('click', event => this.onMobileToggle(event));
       toggler.addEventListener('click', event => this.onMobileToggle(event));
     }
     }
 
 
-    if (window.matchMedia(SIDENAV_DESKTOP_MEDIA).matches) {
-      if (this.state.get('pinned')) {
-        this.pin();
-      } else {
-        this.unpin();
-      }
-    } else {
-      this.bodyRemove('hide');
-      this.bodyAdd('hidden');
-    }
+    const desktopMedia = window.matchMedia(SIDENAV_DESKTOP_MEDIA);
+    this.setResponsiveState(desktopMedia.matches);
+    desktopMedia.addEventListener('change', event => {
+      this.setResponsiveState(event.matches);
+      this.initLinks();
+    });
+
     window.addEventListener('resize', () => this.onResize());
     window.addEventListener('resize', () => this.onResize());
 
 
     this.base.addEventListener('mouseenter', () => this.onEnter());
     this.base.addEventListener('mouseenter', () => this.onEnter());
     this.base.addEventListener('mouseleave', () => this.onLeave());
     this.base.addEventListener('mouseleave', () => this.onLeave());
   }
   }
 
 
+  /**
+   * Apply the appropriate sidenav state for the current responsive layout.
+   */
+  private setResponsiveState(isDesktop: boolean): void {
+    this.bodyRemove('hide');
+
+    if (isDesktop && this.state.get('pinned')) {
+      this.bodyRemove('hidden');
+      this.bodyAdd('show', 'pinned');
+    } else {
+      this.bodyRemove('show', 'pinned');
+      this.bodyAdd('hidden');
+    }
+  }
+
   /**
   /**
    * If the sidenav is shown, expand active nav links. Otherwise, collapse them.
    * If the sidenav is shown, expand active nav links. Otherwise, collapse them.
    */
    */
@@ -153,12 +165,14 @@ class SideNav {
         switch (action) {
         switch (action) {
           case 'expand':
           case 'expand':
             groupLink.setAttribute('aria-expanded', 'true');
             groupLink.setAttribute('aria-expanded', 'true');
+            groupLink.classList.add('show');
             groupItem.classList.add('active');
             groupItem.classList.add('active');
             dropdownMenu.classList.add('show');
             dropdownMenu.classList.add('show');
             link.classList.add('active');
             link.classList.add('active');
             break;
             break;
           case 'collapse':
           case 'collapse':
             groupLink.setAttribute('aria-expanded', 'false');
             groupLink.setAttribute('aria-expanded', 'false');
+            groupLink.classList.remove('show');
             groupItem.classList.remove('active');
             groupItem.classList.remove('active');
             dropdownMenu.classList.remove('show');
             dropdownMenu.classList.remove('show');
             link.classList.remove('active');
             link.classList.remove('active');

Some files were not shown because too many files changed in this diff