diff options
Diffstat (limited to 'src/zenserver/frontend/html/nav.js')
| -rw-r--r-- | src/zenserver/frontend/html/nav.js | 52 |
1 files changed, 49 insertions, 3 deletions
diff --git a/src/zenserver/frontend/html/nav.js b/src/zenserver/frontend/html/nav.js index a5de203f2..759942186 100644 --- a/src/zenserver/frontend/html/nav.js +++ b/src/zenserver/frontend/html/nav.js @@ -11,6 +11,9 @@ * * Each child <a> becomes a nav link. The current page is * highlighted automatically based on the href. + * + * Links may be added or removed dynamically — the component + * re-renders automatically via MutationObserver. */ class ZenNav extends HTMLElement { @@ -18,17 +21,56 @@ class ZenNav extends HTMLElement { connectedCallback() { if (!this.shadowRoot) this.attachShadow({ mode: 'open' }); this._render(); + + this._observer = new MutationObserver(() => this._render()); + this._observer.observe(this, { childList: true }); + } + + disconnectedCallback() { + if (this._observer) { + this._observer.disconnect(); + this._observer = null; + } } _render() { const currentPath = window.location.pathname; + const currentSearch = window.location.search; const items = Array.from(this.querySelectorAll(':scope > a')); + const currentParams = new URLSearchParams(currentSearch); + + let spacerInserted = false; const links = items.map(a => { const href = a.getAttribute('href') || ''; const label = a.textContent.trim(); - const active = currentPath.endsWith(href); - return `<a class="nav-link${active ? ' active' : ''}" href="${href}">${label}</a>`; + const alignRight = a.hasAttribute('data-align') && a.getAttribute('data-align') === 'right'; + let active = false; + + try { + const linkUrl = new URL(href, window.location.origin); + if (linkUrl.pathname === currentPath || currentPath.endsWith(href)) { + // All of the link's query params must be present in the current URL + const linkParams = linkUrl.searchParams; + active = Array.from(linkParams.entries()).every( + ([k, v]) => currentParams.get(k) === v + ); + // A bare path with no params only matches when the current URL also has no page param + if (linkParams.toString() === '' && currentParams.has('page')) { + active = false; + } + } + } catch (e) { + active = currentPath.endsWith(href); + } + + let prefix = ''; + if (alignRight && !spacerInserted) { + prefix = '<span class="nav-spacer"></span>'; + spacerInserted = true; + } + + return `${prefix}<a class="nav-link${active ? ' active' : ''}" href="${href}">${label}</a>`; }).join(''); this.shadowRoot.innerHTML = ` @@ -70,8 +112,12 @@ class ZenNav extends HTMLElement { color: var(--theme_bright); background: var(--theme_g2); } + + .nav-spacer { + flex: 1; + } </style> - <nav class="nav-bar">${links}</nav> + <nav class="nav-bar" part="nav-bar">${links}</nav> `; } } |