fix: close collapsed navigation flyouts

This commit is contained in:
2026-07-31 11:21:52 +02:00
parent 5a2276aec9
commit 10e57c0514
4 changed files with 59 additions and 11 deletions
+3 -1
View File
@@ -32,7 +32,7 @@ Release-Tag oder ein bestimmter Commit verwendet werden:
```bash ```bash
/opt/netbox/venv/bin/pip install --upgrade --force-reinstall \ /opt/netbox/venv/bin/pip install --upgrade --force-reinstall \
"git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.5.1" "git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.5.2"
``` ```
Alternativ kann hinter dem `@` die vollständige Commit-ID stehen. Alternativ kann hinter dem `@` die vollständige Commit-ID stehen.
@@ -209,6 +209,8 @@ ein und wieder aus. Dafür gibt es kein zusätzliches Bedienmenü. Beim Ziehen
wird die neue Breite nach dem Loslassen im Benutzerprofil gespeichert. Im wird die neue Breite nach dem Loslassen im Benutzerprofil gespeichert. Im
Icon-Modus öffnen sich die Menüinhalte als seitliches Flyout; auf kleinen Icon-Modus öffnen sich die Menüinhalte als seitliches Flyout; auf kleinen
beziehungsweise mobilen Ansichten bleibt das normale NetBox-Menü erhalten. beziehungsweise mobilen Ansichten bleibt das normale NetBox-Menü erhalten.
Das Flyout schließt sich bei Auswahl eines Eintrags, bei erneutem Klick auf
das aktive Icon, bei einem Klick außerhalb und mit der Escape-Taste.
**Zurücksetzen** stellt neben Reihenfolge und Sichtbarkeit auch die normale **Zurücksetzen** stellt neben Reihenfolge und Sichtbarkeit auch die normale
ausgeklappte Breite von 288 Pixeln wieder her. ausgeklappte Breite von 288 Pixeln wieder her.
+1 -1
View File
@@ -1,6 +1,6 @@
from netbox.plugins import PluginConfig, get_plugin_config from netbox.plugins import PluginConfig, get_plugin_config
__version__ = "0.5.1" __version__ = "0.5.2"
class NetBoxUtilitiesConfig(PluginConfig): class NetBoxUtilitiesConfig(PluginConfig):
@@ -71,20 +71,40 @@
}); });
} }
function closeDropdown(item) {
if (!item) return false;
const button = item.querySelector(':scope > button.nav-link');
const menu = item.querySelector(':scope > .dropdown-menu');
if (!button || !menu?.classList.contains('show')) return false;
// Let NetBox/Bootstrap tear down its Popper instance when available.
button.click();
if (!menu.classList.contains('show')) {
menu.style.removeProperty('--netbox-utilities-dropdown-top');
return true;
}
// Fallback for pages on which Bootstrap's data API is unavailable.
button.classList.remove('show');
button.setAttribute('aria-expanded', 'false');
menu.classList.remove('show');
menu.style.removeProperty('--netbox-utilities-dropdown-top');
return true;
}
function closeDropdowns(sidebar, exceptItem = null) {
sidebar.querySelectorAll(':scope > li.nav-item.dropdown').forEach((item) => {
if (item !== exceptItem) closeDropdown(item);
});
}
function configureCollapsedDropdowns(sidebar) { function configureCollapsedDropdowns(sidebar) {
sidebar.addEventListener('show.bs.dropdown', (event) => { sidebar.addEventListener('show.bs.dropdown', (event) => {
if (!preferences.collapsed) return; if (!preferences.collapsed) return;
const item = event.target.closest('.nav-item.dropdown'); const item = event.target.closest('.nav-item.dropdown');
if (!item) return; if (!item) return;
sidebar.querySelectorAll(':scope > li.nav-item.dropdown').forEach((otherItem) => { closeDropdowns(sidebar, item);
if (otherItem === item) return;
const otherButton = otherItem.querySelector(':scope > button.nav-link');
const otherMenu = otherItem.querySelector(':scope > .dropdown-menu');
otherButton?.classList.remove('show');
otherButton?.setAttribute('aria-expanded', 'false');
otherMenu?.classList.remove('show');
});
const button = item.querySelector(':scope > button.nav-link'); const button = item.querySelector(':scope > button.nav-link');
const menu = item.querySelector(':scope > .dropdown-menu'); const menu = item.querySelector(':scope > .dropdown-menu');
@@ -92,6 +112,32 @@
const top = Math.max(8, button.getBoundingClientRect().top); const top = Math.max(8, button.getBoundingClientRect().top);
menu.style.setProperty('--netbox-utilities-dropdown-top', `${top}px`); menu.style.setProperty('--netbox-utilities-dropdown-top', `${top}px`);
}); });
sidebar.addEventListener('click', (event) => {
if (!preferences.collapsed || !(event.target instanceof Element)) return;
const selectedOption = event.target.closest('.dropdown-menu a[href], .dropdown-menu button');
if (selectedOption) closeDropdown(selectedOption.closest('.nav-item.dropdown'));
});
document.addEventListener(
'click',
(event) => {
if (!preferences.collapsed || !(event.target instanceof Element)) return;
sidebar.querySelectorAll(':scope > li.nav-item.dropdown').forEach((item) => {
if (!item.contains(event.target)) closeDropdown(item);
});
},
true
);
document.addEventListener('keydown', (event) => {
if (!preferences.collapsed || event.key !== 'Escape') return;
const openItem = sidebar.querySelector(':scope > li.nav-item.dropdown > .dropdown-menu.show')?.closest('.nav-item');
if (openItem && closeDropdown(openItem)) {
openItem.querySelector(':scope > button.nav-link')?.focus();
event.preventDefault();
}
});
} }
async function saveLayout(action, width) { async function saveLayout(action, width) {
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "netbox-utilities" name = "netbox-utilities"
version = "0.5.1" version = "0.5.2"
description = "Navigation, tenant filtering, bulk module installation, and atomic rack reordering for NetBox 4.6" description = "Navigation, tenant filtering, bulk module installation, and atomic rack reordering for NetBox 4.6"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"