UNPKG

@cagovweb/cal-ds-header

Version:
402 lines (346 loc) 11.6 kB
// from // https://www.cssscript.com/create-a-multi-level-drop-down-menu-with-pure-css/ //@ts-check import cal_ds_base from "../../_cal-ds-base/src/index"; // @ts-ignore import css from "./styles.css"; // @ts-ignore import html from "./template.html"; export default class my extends cal_ds_base { /** @override */ static get tagName() { return "cal-ds-header"; } /** * @protected * @override */ static get observedAttributes() { return ["data-logo-overflow", "data-apps-link-style"]; } constructor() { /** * @param {Element} target * @param {Element} source * @private */ const _updateAttributes = (target, source) => { if (source.attributes) // Update attributes // Clear attribtues set as "null" Array.from(source.attributes).forEach(attr => { if (attr.value.trim().toLowerCase() === "null") { target.attributes.removeNamedItem(attr.name); } else { target.setAttribute(attr.name, attr.value); } }); // Update text content if specified if ( !target.childElementCount && !source.childElementCount && source.textContent?.trim() ) { target.textContent = source.textContent; } }; /** * * @param {Element} target * @param {Element} source * @param {boolean} children * @private */ const _updateElement = (target, source, children = false) => { // Update attributes if specified _updateAttributes(target, source); if (!children) return; // Add missing children Array.from(source.children).forEach(sourceChild => { if (sourceChild.nodeType === Node.ELEMENT_NODE) { const targetChild = Array.from(target.children).find( child => child.tagName === sourceChild.tagName ); if (targetChild) { _updateElement(targetChild, sourceChild, children); } else { target.appendChild(sourceChild.cloneNode(true)); } } }); }; /** * @template {HTMLElement} T * @param {DocumentFragment | Element} element * @param {string} selectors * @private */ const _querySelectorRequre = (element, selectors) => { const result = element.querySelector(selectors); if (!result) throw Error(`Can't find selector "${selectors}"`); return /** @type {T} */ (result); }; /** * * @param {HTMLElement} source * @param {HTMLElement} target_desktop_nav_menu * @param {HTMLElement} target_mobile_nav_menu */ const _processNav = ( source, target_desktop_nav_menu, target_mobile_nav_menu ) => { const source_navs = source.querySelectorAll(":scope > nav"); if (!source_navs.length) { target_mobile_nav_menu.remove(); target_desktop_nav_menu.remove(); return; } const validUrl = (/** @type {string} */ href) => { try { return new URL(href, window.location.origin).href; } catch (e) { return href; } }; const setIfCurrent = (/** @type {HTMLAnchorElement} */ a) => { if (validUrl(a.href) === window.location.href) { a.ariaCurrent = "page"; } }; const getLi = ( /** @type {Element} */ myTag, /** @type {HTMLTemplateElement} */ myTemplate ) => { if (myTag.tagName === "A") { const newLi = /** @type {HTMLElement} */ ( myTemplate.content.cloneNode(true) ); /** @type {HTMLAnchorElement} */ const aTag = _querySelectorRequre(newLi, "a"); _updateElement(aTag, myTag); setIfCurrent(aTag); return newLi; } else { return document.createElement("li"); } }; /** @type {HTMLUListElement} */ const target_mobile_nav_ul = _querySelectorRequre( target_mobile_nav_menu, ":scope > ul" ); /** @type {HTMLUListElement} */ const target_desktop_nav_ul_utility = _querySelectorRequre( target_desktop_nav_menu, ":scope > ul.desktop-nav-menu-utility" ); /** @type {HTMLUListElement} */ const target_desktop_nav_ul_main = _querySelectorRequre( target_desktop_nav_menu, ":scope > ul.desktop-nav-menu-main" ); /** @type {HTMLTemplateElement} */ const templateMobile = _querySelectorRequre( target_mobile_nav_ul, "template" ); /** @type {HTMLTemplateElement} */ const templateDesktopMain = _querySelectorRequre( target_desktop_nav_ul_main, "template" ); //Removing templates once found. Not needed for output markup templateMobile.remove(); templateDesktopMain.remove(); [...source_navs[0].children].forEach(n => { target_mobile_nav_ul.appendChild(getLi(n, templateMobile)); target_desktop_nav_ul_main.appendChild(getLi(n, templateDesktopMain)); }); const source_nav_utility = source_navs.length > 1 ? source_navs[1] : undefined; if (source_nav_utility) { /** @type {HTMLTemplateElement} */ const templateDesktopUtility = _querySelectorRequre( target_desktop_nav_ul_utility, "template" ); templateDesktopUtility.remove(); [...source_nav_utility.children].forEach(n => { target_mobile_nav_ul.appendChild(getLi(n, templateMobile)); target_desktop_nav_ul_utility.appendChild( getLi(n, templateDesktopUtility) ); }); } else { target_desktop_nav_ul_utility.remove(); } }; /** * * @param {HTMLDivElement} source * @param {HTMLElement} target_site_header_container * @param {my} me */ const _processBranding = (source, target_site_header_container, me) => { const source_site_logo = source.querySelector(":scope > a:first-of-type"); if (source_site_logo) { // <a class="site-logo"> const target_site_logo = _querySelectorRequre( target_site_header_container, ":scope > a.site-logo" ); _updateElement(target_site_logo, source_site_logo); // <img class="logo-image" /> const target_site_logo_img = _querySelectorRequre( target_site_logo, ":scope > img.logo-image" ); const source_site_logo_img = source_site_logo.querySelector(":scope > img"); if (source_site_logo_img) { _updateElement(target_site_logo_img, source_site_logo_img); } if (me.dataset.logoOverflow?.toLowerCase() === "false") { target_site_logo_img.classList.add("no-overflow"); } const source_site_branding_spans = source_site_logo.querySelectorAll(":scope > span"); if (source_site_branding_spans.length) { // <div class="site-branding-text"> const target_site_branding = _querySelectorRequre( target_site_logo, ":scope > div.site-branding-text" ); // <span class="state"> const target_site_branding_state = _querySelectorRequre( target_site_branding, ":scope > span.state" ); _updateElement( target_site_branding_state, source_site_branding_spans[0] ); // <span class="department"> const target_site_branding_department = _querySelectorRequre( target_site_branding, ":scope > span.department" ); if (source_site_branding_spans.length > 1) { _updateElement( target_site_branding_department, source_site_branding_spans[1] ); } else { target_site_branding_department.innerHTML = ""; } } } }; /** * * @param {HTMLDivElement} source * @param {HTMLElement} target_site_header_utility * @private */ const _processSearch = (source, target_site_header_utility) => { // <div class="search-container-desktop"> const target_search_container_desktop = _querySelectorRequre( target_site_header_utility, ":scope > div.search-container-desktop" ); /** @type {HTMLFormElement | null} */ const source_form = source.querySelector(":scope > form"); if (source_form) { // <form> const target_form = _querySelectorRequre( target_search_container_desktop, ":scope > form" ); _updateElement(target_form, source_form, true); } else { // No form specified, remove search target_search_container_desktop.remove(); } }; /** * Used with `observedAttributes` to track attribute changes * @protected */ const _attributeChangedCallback = () => { //switch (_name) { // case my.observedAttributes[0]: //"data-logo-overflow"; // case my.observedAttributes[1]: //"data-apps-link-style"; _contentChanged(); // break; //} }; const _contentChanged = () => { if (this.UserTemplate && this.shadowRoot) { const target = this.shadowRoot; target.innerHTML = html; const source = document.createElement("div"); source.appendChild(this.UserTemplate.cloneNode(true)); // <header role="banner"> // <div class="site-header"> // <div class="site-header-container"> const target_site_header_container = _querySelectorRequre( target, "header > div.site-header > div.site-header-container" ); // <div class="site-header-utility"> const target_site_header_utility = _querySelectorRequre( target_site_header_container, ":scope > div.site-header-utility" ); _processBranding(source, target_site_header_container, this); _processSearch(source, target_site_header_utility); _processNav( source, _querySelectorRequre(target, "header > nav.desktop-nav-menu"), _querySelectorRequre( target_site_header_container, ":scope > nav.mobile-nav-menu" ) ); // ca-gov menu // data-apps-link-style="none" if (this.dataset.appsLinkStyle?.toLowerCase() === "none") { /** @type {HTMLAnchorElement} */ ( target.querySelector("#cagov-toggle") ).remove(); } // Process Login Button const source_login_button = source.querySelector( ":scope > a.login-button" ); const target_login_button = _querySelectorRequre( target_site_header_utility, ":scope > a.login-button" ); if (source_login_button) { _updateElement(target_login_button, source_login_button, true); } else { target_login_button.remove(); } } }; // Track changes to the URL and update the selected menu item /** @private */ let lastUrl = window.location.href; setInterval(() => { if (window.location.href !== lastUrl) { lastUrl = window.location.href; _contentChanged(); } }, 1000); super({ shadow: true, css, connectedCallback: _contentChanged, templateChangedCallback: _contentChanged, attributeChangedCallback: _attributeChangedCallback }); } }