diff --git a/public/navigation-api.js b/public/navigation-api.js new file mode 100644 index 0000000..2f25fd8 --- /dev/null +++ b/public/navigation-api.js @@ -0,0 +1,84 @@ +(() => { + 'use strict'; + + const root = document.documentElement; + if (root.dataset.uxNavigationApiReady === '1') return; + root.dataset.uxNavigationApiReady = '1'; + + let observer = null; + let lastView = ''; + + function activeView() { + return document.querySelector('.sidebar-nav .nav-item.active[data-tab]')?.dataset.tab + || document.querySelector('.tab-content.active')?.id + || 'dashboard'; + } + + function navItem(view) { + const normalized = String(view || '').trim(); + if (!normalized) return null; + const items = document.querySelectorAll('.sidebar-nav .nav-item[data-tab]'); + return [...items].find(item => item.dataset.tab === normalized) || null; + } + + function canOpen(view) { + const item = navItem(view); + return Boolean(item + && !item.hidden + && !item.classList.contains('hidden') + && item.getAttribute('aria-hidden') !== 'true' + && item.getAttribute('aria-disabled') !== 'true'); + } + + function open(view) { + const item = navItem(view); + if (!canOpen(view) || typeof item?.click !== 'function') return false; + item.click(); + return true; + } + + function emitChange(source = 'observer') { + const currentView = activeView(); + if (currentView === lastView) return; + const previousView = lastView || null; + lastView = currentView; + window.dispatchEvent(new CustomEvent('vendoo:navigation-changed', { + detail: { currentView, previousView, source }, + })); + } + + const api = {}; + Object.defineProperties(api, { + currentView: { + enumerable: true, + get: activeView, + }, + canOpen: { + enumerable: true, + value: canOpen, + }, + open: { + enumerable: true, + value: open, + }, + }); + window.VendooNavigation = Object.freeze(api); + + function observe() { + const nav = document.querySelector('.sidebar-nav'); + if (!nav || observer) return; + lastView = activeView(); + observer = new MutationObserver(() => emitChange('observer')); + observer.observe(nav, { + subtree: true, + attributes: true, + attributeFilter: ['class', 'hidden', 'aria-hidden', 'aria-disabled'], + }); + } + + observe(); + window.addEventListener('beforeunload', () => observer?.disconnect(), { once: true }); + window.dispatchEvent(new CustomEvent('vendoo:navigation-api-ready', { + detail: { currentView: activeView(), version: '1.44.0' }, + })); +})();