Просмотр исходного кода

#6797: Automatically collapse inactive sections in the sidenav

Matt 4 лет назад
Родитель
Сommit
da67a35328

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/config.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/config.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/jobs.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/jobs.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/lldp.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/lldp.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/netbox.js.map


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/status.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
netbox/project-static/dist/status.js.map


+ 2 - 1
netbox/project-static/src/bs.ts

@@ -1,4 +1,4 @@
-import { Modal, Tab, Toast, Tooltip } from 'bootstrap';
+import { Collapse, Modal, Tab, Toast, Tooltip } from 'bootstrap';
 import Masonry from 'masonry-layout';
 import { getElements } from './util';
 
@@ -6,6 +6,7 @@ type ToastLevel = 'danger' | 'warning' | 'success' | 'info';
 
 // Add common Bootstrap components to `window`, so they may be consumed globally (primarily for
 // plugins).
+window.Collapse = Collapse;
 window.Modal = Modal;
 window.Toast = Toast;
 window.Tooltip = Tooltip;

+ 5 - 0
netbox/project-static/src/global.d.ts

@@ -7,6 +7,11 @@ type Dict<T extends unknown = unknown> = Record<string, T>;
 type Nullable<T> = T | null;
 
 interface Window {
+  /**
+   * Bootstrap Collapse Instance.
+   */
+  Collapse: typeof import('bootstrap').Collapse;
+
   /**
    * Bootstrap Modal Instance.
    */

+ 68 - 0
netbox/project-static/src/sidenav.ts

@@ -1,8 +1,10 @@
+import { Collapse } from 'bootstrap';
 import { StateManager } from './state';
 import { getElements, isElement } from './util';
 
 type NavState = { pinned: boolean };
 type BodyAttr = 'show' | 'hide' | 'hidden' | 'pinned';
+type Section = [HTMLAnchorElement, InstanceType<typeof Collapse>];
 
 class SideNav {
   /**
@@ -15,6 +17,16 @@ class SideNav {
    */
   private state: StateManager<NavState>;
 
+  /**
+   * The currently active parent nav-link controlling a section.
+   */
+  private activeLink: Nullable<HTMLAnchorElement> = null;
+
+  /**
+   * All collapsible sections and their controlling nav-links.
+   */
+  private sections: Section[] = [];
+
   constructor(base: HTMLDivElement) {
     this.base = base;
     this.state = new StateManager<NavState>(
@@ -23,6 +35,7 @@ class SideNav {
     );
 
     this.init();
+    this.initSectionLinks();
     this.initLinks();
   }
 
@@ -97,11 +110,17 @@ class SideNav {
     }
   }
 
+  /**
+   * Show the sidenav.
+   */
   private show(): void {
     this.bodyAdd('show');
     this.bodyRemove('hidden', 'hide');
   }
 
+  /**
+   * Hide the sidenav and collapse all active nav sections.
+   */
   private hide(): void {
     this.bodyAdd('hidden');
     this.bodyRemove('pinned', 'show');
@@ -131,6 +150,51 @@ class SideNav {
     this.state.set('pinned', false);
   }
 
+  /**
+   * When a section's controlling nav-link is clicked, update this instance's `activeLink`
+   * attribute and close all other sections.
+   */
+  private handleSectionClick(event: Event): void {
+    event.preventDefault();
+    const element = event.target as HTMLAnchorElement;
+    this.activeLink = element;
+    this.closeInactiveSections();
+  }
+
+  /**
+   * Close all sections that are not associated with the currently active link (`activeLink`).
+   */
+  private closeInactiveSections(): void {
+    for (const [link, collapse] of this.sections) {
+      if (link !== this.activeLink) {
+        link.classList.add('collapsed');
+        link.setAttribute('aria-expanded', 'false');
+        collapse.hide();
+      }
+    }
+  }
+
+  /**
+   * Initialize `bootstrap.Collapse` instances on all section collapse elements and add event
+   * listeners to the controlling nav-links.
+   */
+  private initSectionLinks(): void {
+    for (const section of getElements<HTMLAnchorElement>(
+      '.navbar-nav .nav-item .nav-link[data-bs-toggle]',
+    )) {
+      if (section.parentElement !== null) {
+        const collapse = section.parentElement.querySelector<HTMLDivElement>('.collapse');
+        if (collapse !== null) {
+          const collapseInstance = new Collapse(collapse, {
+            toggle: false, // Don't automatically open the collapse element on invocation.
+          });
+          this.sections.push([section, collapseInstance]);
+          section.addEventListener('click', event => this.handleSectionClick(event));
+        }
+      }
+    }
+  }
+
   /**
    * Starting from the bottom-most active link in the element tree, work backwards to determine the
    * link's containing `.collapse` element and the `.collapse` element's containing `.nav-link`
@@ -232,6 +296,10 @@ class SideNav {
     }
   }
 
+  /**
+   * Handle sidenav visibility state for small screens. On small screens, there is no pinned state,
+   * only open/closed.
+   */
   private onMobileToggle(event: Event): void {
     event.preventDefault();
     if (this.bodyHas('hidden')) {

+ 0 - 4
netbox/project-static/styles/sidenav.scss

@@ -271,10 +271,6 @@
         white-space: nowrap;
         transition: $transition-100ms-ease-in-out;
 
-        // &.disabled {
-        //   opacity: 0.8;
-        // }
-
         &.active {
           position: relative;
           color: var(--nbx-sidebar-link-hover-bg);

Некоторые файлы не были показаны из-за большого количества измененных файлов