← back to Nationalrealestate

public/nav-drawer.js

83 lines

/* nav-drawer.js — turns the page's #topbar into an upper-left hamburger drawer.
 * It MOVES the topbar's existing child nodes into the drawer (same DOM nodes), so
 * every id and event binding is preserved and page scripts that populate #metrics /
 * wire #typeSeg keep working. Include on any page that has a <div id="topbar">. */
(function () {
  function init() {
    if (document.getElementById('navBurger')) return; // idempotent
    var bar = document.getElementById('topbar');

    var burger = document.createElement('button');
    burger.id = 'navBurger';
    burger.type = 'button';
    burger.setAttribute('aria-label', 'Menu');
    burger.setAttribute('aria-expanded', 'false');
    burger.textContent = '☰'; // ☰

    var backdrop = document.createElement('div');
    backdrop.id = 'navBackdrop';

    var drawer = document.createElement('div');
    drawer.id = 'navDrawer';
    var body = document.createElement('div');
    body.id = 'navDrawerBody';
    drawer.appendChild(body);

    // Relocate the whole topbar into the drawer (preserve nodes/ids/bindings).
    if (bar) {
      while (bar.firstChild) body.appendChild(bar.firstChild);
      bar.parentNode && bar.parentNode.removeChild(bar);
    }

    // Canonical nav — render one identical link set on every page (marks the
    // current page active by URL) so the nav never drifts page-to-page.
    var NAV = [
      ['/', 'Map'], ['/markets.html', 'Markets'], ['/commercial.html', 'Commercial'],
      ['/compare.html', 'Compare'], ['/brokers.html', 'Brokers'], ['/sources.html', 'Sources'],
      ['/watchlist.html', 'Watchlist'], ['/admin.html', 'Admin']
    ];
    var here = location.pathname.replace(/\/index\.html$/, '/');
    var nav = body.querySelector('nav');
    if (!nav) {
      nav = document.createElement('nav');
      var brand = body.querySelector('.brand');
      if (brand && brand.nextSibling) body.insertBefore(nav, brand.nextSibling);
      else body.insertBefore(nav, body.firstChild);
    }
    nav.innerHTML = NAV.map(function (x) {
      var active = x[0] === here || (x[0] === '/' && (here === '' || here === '/'));
      return '<a href="' + x[0] + '"' + (active ? ' class="active"' : '') + '>' + x[1] + '</a>';
    }).join('');

    document.body.appendChild(backdrop);
    document.body.appendChild(drawer);
    document.body.appendChild(burger);

    // Clearance so the fixed hamburger never sits on top of page content.
    // Grid pages clear it via .cg-rail padding (see nav-drawer.css); normal
    // flow-layout pages get body top-padding. The absolute map has no .cg-shell
    // and is out of flow, so the padding is harmless there.
    if (!document.querySelector('.cg-shell')) document.body.classList.add('navdz-pad');

    function open() {
      drawer.classList.add('open'); backdrop.classList.add('open'); burger.classList.add('open');
      burger.textContent = '✕'; // ✕
      burger.setAttribute('aria-expanded', 'true');
    }
    function close() {
      drawer.classList.remove('open'); backdrop.classList.remove('open'); burger.classList.remove('open');
      burger.textContent = '☰';
      burger.setAttribute('aria-expanded', 'false');
    }
    burger.addEventListener('click', function () {
      drawer.classList.contains('open') ? close() : open();
    });
    backdrop.addEventListener('click', close);
    // clicking a nav link navigates away — close so it isn't left open on the next page
    body.addEventListener('click', function (e) { if (e.target.closest('nav a')) close(); });
    document.addEventListener('keydown', function (e) { if (e.key === 'Escape') close(); });
  }
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
  else init();
})();