mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
#6797: Automatically collapse inactive sections in the sidenav
This commit is contained in:
parent
0ea9c65007
commit
da67a35328
BIN
netbox/project-static/dist/config.js
vendored
BIN
netbox/project-static/dist/config.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/config.js.map
vendored
BIN
netbox/project-static/dist/config.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/jobs.js
vendored
BIN
netbox/project-static/dist/jobs.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/jobs.js.map
vendored
BIN
netbox/project-static/dist/jobs.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/lldp.js
vendored
BIN
netbox/project-static/dist/lldp.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/lldp.js.map
vendored
BIN
netbox/project-static/dist/lldp.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
Binary file not shown.
BIN
netbox/project-static/dist/status.js
vendored
BIN
netbox/project-static/dist/status.js
vendored
Binary file not shown.
BIN
netbox/project-static/dist/status.js.map
vendored
BIN
netbox/project-static/dist/status.js.map
vendored
Binary file not shown.
@ -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
netbox/project-static/src/global.d.ts
vendored
5
netbox/project-static/src/global.d.ts
vendored
@ -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.
|
||||
*/
|
||||
|
@ -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')) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user