From 76f48023823094f940a60c82b5e1bcf7f1b26aa2 Mon Sep 17 00:00:00 2001 From: Nathan Rogan Date: Mon, 19 Oct 2020 10:16:32 +0100 Subject: [PATCH] fix: highlight js by moving header js --- src/www/_partials/_scripts.njk | 5 +- .../component-example/_component-example.js | 77 +++--- src/www/wmcads-website.js | 259 +++++++++++++++++- 3 files changed, 301 insertions(+), 40 deletions(-) diff --git a/src/www/_partials/_scripts.njk b/src/www/_partials/_scripts.njk index 187d72e1..d3cc0fa7 100755 --- a/src/www/_partials/_scripts.njk +++ b/src/www/_partials/_scripts.njk @@ -1,3 +1,6 @@ + + + @@ -11,5 +14,3 @@ gtag('config', 'UA-151672610-1'); - - \ No newline at end of file diff --git a/src/www/_partials/component-example/_component-example.js b/src/www/_partials/component-example/_component-example.js index 15de4cec..cc4882fd 100755 --- a/src/www/_partials/component-example/_component-example.js +++ b/src/www/_partials/component-example/_component-example.js @@ -1,50 +1,57 @@ -function showCode() { +function showCode(ele) { // for each button we create below - document.querySelectorAll('.wmcads-js-show-code').forEach(ele => { - function showMore(e) { - const btn = e.target || e.srcElement; // Set btn to the element from which the click came - - // if the btn clicked contains this class, then it must be active, so reset it back to the norm - if (btn.classList.contains('wmcads-js-show-code--active')) { - btn.previousSibling.querySelector('.hljs').style.maxHeight = '200px'; - btn.classList.remove('wmcads-js-show-code--active'); - btn.innerText = 'Show more code'; - } else { - // Else expand the code preview to 100% - btn.previousSibling.querySelector('.hljs').style.maxHeight = '100%'; - btn.classList.add('wmcads-js-show-code--active'); - btn.innerText = 'Show less code'; - } + function showMore(e) { + const btn = e.target || e.srcElement; // Set btn to the element from which the click came + + // if the btn clicked contains this class, then it must be active, so reset it back to the norm + if (btn.classList.contains('wmnds-js-show-code--active')) { + btn.previousSibling.querySelector('.hljs').style.maxHeight = '200px'; + btn.classList.remove('wmnds-js-show-code--active'); + btn.innerText = 'Show more code'; + } else { + // Else expand the code preview to 100% + btn.previousSibling.querySelector('.hljs').style.maxHeight = '100%'; + btn.classList.add('wmnds-js-show-code--active'); + btn.innerText = 'Show less code'; } + } + + // when clicked + ele.addEventListener('click', e => { + e.preventDefault(); + showMore(e); + }); - // when clicked - ele.addEventListener('click', e => { + // When keyboard "enter" pressed + ele.addEventListener('keydown', e => { + if (e.keyCode === 13) { e.preventDefault(); showMore(e); - }); - - // When keyboard "enter" pressed - ele.addEventListener('keydown', e => { - if (e.keyCode === 13) { - e.preventDefault(); - showMore(e); - } - }); + } }); } +export { showCode }; + +const displayShowMore = element => { + // If the code preview is 192 height (without padding) then we need to display the 'show more code' button + if (element.clientHeight >= 192 && !element.classList.contains('wmnds-show-more-ignore')) { + const htmlString = 'Show more code'; + + element.parentElement.insertAdjacentHTML('afterend', htmlString); + } +}; + +export { displayShowMore }; + export default () => { const { hljs } = window; // declare higlightJS as global var document.querySelectorAll('pre code').forEach(element => { // Run highlightJS for each pre code element found */ hljs.highlightBlock(element); - // If the code preview is 192 height (without padding) then we need to display the 'show more code' button - if (element.clientHeight >= 192 && !element.classList.contains('wmcads-show-more-ignore')) { - const htmlString = 'Show more code'; - - element.parentElement.insertAdjacentHTML('afterend', htmlString); - } + displayShowMore(element); + }); + document.querySelectorAll('.wmnds-js-show-code').forEach(ele => { + showCode(ele); // run show code function above when hljs has init }); - - showCode(); // run show code function above when hljs has init }; diff --git a/src/www/wmcads-website.js b/src/www/wmcads-website.js index 4c6d1035..3afaec1c 100755 --- a/src/www/wmcads-website.js +++ b/src/www/wmcads-website.js @@ -1,7 +1,6 @@ import forEachPolyfill from './assets/vendor/js/polyfills/ie11-forEach'; import colorPalettes from './pages/styles/colour-palettes/_color-palettes'; -import highlightJS from './_partials/component-example/_component-example'; -import header from '../wmcads/patterns/header/_header'; +import highlight from './_partials/component-example/_component-example'; const icons = () => { // Ajax SVG in, SVGS are referenced in app (Icon component) @@ -16,4 +15,258 @@ const icons = () => { }; }; -window.addEventListener('DOMContentLoaded', (forEachPolyfill, icons(), colorPalettes, highlightJS, header)); +const headerJs = () => { + // get mega menu elements + const megaMenus = document.querySelectorAll('.wmcads-mega-menu'); + + const mobileMenu = window.matchMedia('(max-width: 767px)'); + + [].forEach.call(megaMenus, menu => { + // mobile nav function + function handleMobileMenu(mq) { + if (mq.matches) { + const mobileToggle = menu.querySelector('.wmcads-mega-menu__mobile-toggle'); + const headerEl = menu.parentNode.parentNode; + + const topLevelMenuBtn = menu.querySelectorAll('.wmcads-mega-menu__link-arrow-icon-btn'); + const mobileMenuIsOpen = { menu: false, primary: false }; + + // handle mobile menu toggle + mobileToggle.addEventListener('click', () => { + mobileMenuIsOpen.menu = !mobileMenuIsOpen.menu; + if (mobileMenuIsOpen.menu) { + headerEl.classList.add('wmcads-header--mega-menu-open'); + document.querySelector('html').classList.add('mobile-menu-open'); + } else { + headerEl.classList.remove('wmcads-header--mega-menu-open', 'wmcads-header--mega-menu-submenu-open'); + document.querySelector('html').classList.remove('mobile-menu-open'); + } + }); + + // handle sub menu open/close + [].forEach.call(topLevelMenuBtn, menuBtn => { + menuBtn.addEventListener('click', () => { + mobileMenuIsOpen.primary = !mobileMenuIsOpen.primary; + const targetListItem = menuBtn.parentNode; + if (mobileMenuIsOpen.primary) { + targetListItem.classList.add('open'); + targetListItem.querySelector('.wmcads-mega-menu__sub-menu-link').focus(); + headerEl.classList.add('wmcads-header--mega-menu-submenu-open'); + } else { + targetListItem.classList.remove('open'); + headerEl.classList.remove('wmcads-header--mega-menu-submenu-open'); + } + }); + }); + + // mobile collapse for third level menus + const collapseMenus = menu.querySelectorAll( + '.wmcads-mega-menu__sub-menu-item .wmcads-mega-menu__collapse-toggle' + ); + [].forEach.call(collapseMenus, collapseToggle => { + collapseToggle.addEventListener('click', () => { + const panel = collapseToggle.nextElementSibling; + collapseToggle.classList.toggle('open'); + if (panel.style.maxHeight) { + panel.style.maxHeight = null; + } else { + panel.style.maxHeight = `${panel.scrollHeight}px`; + } + }); + }); + } + } + // end mobile nav function + + // init mobile nav function + handleMobileMenu(mobileMenu); + mobileMenu.addListener(handleMobileMenu); + + const topLevelLinks = menu.querySelectorAll('.wmcads-mega-menu__primary-menu-link'); + + // handle events within each top level list item + [].forEach.call(topLevelLinks, (topLevelLink, topLevelLinkIndex) => { + // return list item parent of the current link if it exists else return the link + const topLevelListItem = topLevelLink.parentNode.tagName === 'LI' ? topLevelLink.parentNode : topLevelLink; + const subMenuLinks = topLevelListItem.querySelectorAll('.wmcads-mega-menu__sub-menu-link'); + + // check if level 3 menus are present, if so add modifier class + const hasSubmenuChildren = + topLevelListItem.querySelectorAll('.wmcads-mega-menu__sub-menu-child-menu').length !== 0; + if (hasSubmenuChildren) { + topLevelListItem.querySelectorAll('.wmcads-mega-menu__sub-menu').forEach(subMenu => { + subMenu.classList.add('wmcads-mega-menu__sub-menu--has-child-menus'); + }); + } + + const clearActiveListItems = () => { + // remove active classes from other list items + [].forEach.call(menu.querySelectorAll('.wmcads-mega-menu__primary-menu-item'), menuItem => { + menuItem.classList.remove('active'); + }); + }; + + // handle setting the active class on menu and list items + const setMenuActive = (active, focusLink) => { + if (active !== false) { + menu.classList.add('active'); + clearActiveListItems(); + // add active class to current item + topLevelListItem.classList.add('active'); + } else { + menu.classList.remove('active'); + topLevelListItem.classList.remove('active'); + // set focus on menu close + if (focusLink !== false) { + topLevelLink.focus(); + } + } + }; + + // returns a specified menu link from a specified array + // currentIndex = index of the link that is currently focused + // array = array to move through + // direction = next, prev, + const getMenuLink = (currentIndex, array, direction) => { + let menuLink = null; + if (array) { + if (direction === 'prev') { + // return previous link in specified array if there is one else return null; + menuLink = array[currentIndex - 1] ? array[currentIndex - 1] : null; + } else if (direction === 'next') { + // return next link in specified array if there is one else return null; + menuLink = array[currentIndex + 1] ? array[currentIndex + 1] : null; + } else { + // return link with same index in specified array; + menuLink = array[currentIndex] ? array[currentIndex] : array[array.length - 1]; + } + } + return menuLink; + }; + + const handleKeydown = (e, key) => { + e.stopPropagation(); + // if key pressed is enter, space bar or down arrow + if (key === 13 || key === 32 || key === 40) { + // check if list item has a mega menu + const openSubMenu = () => { + if (topLevelListItem.querySelectorAll('.wmcads-mega-menu__container').length) { + e.preventDefault(); + // remove keyFocus to allow menu to show + setMenuActive(true); + // focus first menu item + subMenuLinks[0].focus(); + } + }; + // enter + // check if link exists + if (key === 13) { + if (!topLevelLink.tagName === 'a' || !topLevelLink.getAttribute('href')) { + openSubMenu(); + } + } else { + openSubMenu(); + } + } else if (key === 37) { + // left arrow + const prevLink = getMenuLink(topLevelLinkIndex, topLevelLinks, 'prev'); + if (prevLink) prevLink.focus(); + } else if (key === 39) { + // right arrow + const nextLink = getMenuLink(topLevelLinkIndex, topLevelLinks, 'next'); + if (nextLink) nextLink.focus(); + } else if (key === 27) { + // if escape pressed + setMenuActive(false); + } + }; + + // if top level link doesn't have a mega-menu child add class to menu to hide overlay when hovered + // has to be added/removed on mouseover to cover menus that have a mix of items with/without mega menus + const isTopLevelWithMenu = topLevelListItem.querySelectorAll('.wmcads-mega-menu__container').length; + + if (isTopLevelWithMenu) { + topLevelLink.addEventListener('mouseover', () => { + setMenuActive(); + }); + topLevelListItem.addEventListener('mouseleave', () => { + setMenuActive(false, false); + clearActiveListItems(); + }); + + // array of mega menu links by column + topLevelListItem.addEventListener('blur', setMenuActive(false, false)); + } + + topLevelListItem.addEventListener('keydown', e => { + handleKeydown(e, e.keyCode); + }); + // top lvl link event listeners + topLevelLink.addEventListener('mousedown', e => { + // prevent link focus on click + e.preventDefault(); + // setMenuActive(false); + }); + // topLevelLink.addEventListener('focus', handleKeyFocus); + + const menuArray = []; + [].forEach.call(subMenuLinks, (menuLink, menuIndex) => { + const thisList = menuLink.parentNode; + const thisListLinks = thisList.querySelectorAll('a'); + // push list of links to array + menuArray.push(thisListLinks); + + [].forEach.call(thisListLinks, (link, linkIndex) => { + link.addEventListener('keydown', e => { + if (e.keyCode !== 27) { + e.stopPropagation(); + if (e.keyCode === 39) { + // right arrow - go to link of same index in next menu list + e.preventDefault(); + const nextMenuLink = getMenuLink(linkIndex, menuArray[menuIndex + 1]); + if (nextMenuLink) nextMenuLink.focus(); + } else if (e.keyCode === 37) { + // left arrow - go to link of same index in previous menu list + e.preventDefault(); + const prevMenuLink = getMenuLink(linkIndex, menuArray[menuIndex - 1]); + if (prevMenuLink) prevMenuLink.focus(); + } else if (e.keyCode === 40) { + // down arrow - go to next link in current menu list + e.preventDefault(); + // if next link doesn't exist try next menu first item else return null + const nextLink = getMenuLink(linkIndex, thisListLinks, 'next') + ? getMenuLink(linkIndex, thisListLinks, 'next') + : getMenuLink(-1, menuArray[menuIndex + 1], 'next'); + if (nextLink) { + nextLink.focus(); + } else { + setMenuActive(false); + if (getMenuLink(topLevelLinkIndex, topLevelLinks, 'next')) { + getMenuLink(topLevelLinkIndex, topLevelLinks, 'next').focus(); + } + } + } else if (e.keyCode === 38) { + // up arrow - go to previous item in current menu list + e.preventDefault(); + const prevMenu = menuArray[menuIndex - 1]; + let prevLink = null; + if (prevMenu || linkIndex > 0) { + prevLink = getMenuLink(linkIndex, thisListLinks, 'prev') + ? getMenuLink(linkIndex, thisListLinks, 'prev') + : getMenuLink(prevMenu.length, prevMenu, 'prev'); + } + if (prevLink) { + prevLink.focus(); + } else { + setMenuActive(false); + } + } + } + }); + }); + }); + }); + }); +}; + +window.addEventListener('DOMContentLoaded', (forEachPolyfill, icons(), headerJs(), colorPalettes, highlight));