UNPKG

@sgalinski/responsive-side-menu

Version:

A simple and extendable slide-in menu

149 lines (142 loc) 5.45 kB
import Header from './header'; import Settings from './settings'; /** * This class is responsible for generating the menus markup */ export default class MarkupGenerator { static generateMenuStub(_rootElement) { this.subMenuCount = 0; let menu = document.createElement('aside'); menu.classList.add('rsm'); menu.classList.add(`rsm-orientation-${Settings.get.orientation}`); menu.setAttribute('id', 'rsm'); document.body.appendChild(menu); let slideMenu = this.extractList(_rootElement.querySelector('ul')); let header = new Header(); slideMenu.insertBefore(header.Element, slideMenu.childNodes[0]); menu.appendChild(slideMenu); return { menu, slideMenu }; } static extractList(_list, _subMenuId = null, backLinkLabel = Settings.get.backLinkLabel) { let list = document.createElement('ul'); list.setAttribute('class', 'rsm-root'); if (_subMenuId !== null) { let backLinkItem = document.createElement('li'); backLinkItem.classList.add('rsm-back-link'); let backLink = document.createElement('a'); backLink.innerHTML = `<span class="rsm-back-link-title">${backLinkLabel}</span>`; backLink.setAttribute('href', `#${_subMenuId}`); backLinkItem.appendChild(backLink); list.appendChild(backLinkItem); } Array.from(_list.childNodes).forEach(node => { if (node.nodeName === 'LI') { let nextBackLinkLabel = backLinkLabel; // Checking for _subMenuId to skip the root level entries (they are showing the default label anyway) if (Settings.get.useParentSectionLinkTextAsBackLinkLabel && _subMenuId) { try { nextBackLinkLabel = node.parentNode.parentNode.querySelector('a').firstChild.textContent.trim(); } catch (e) { // eslint-disable-next-line no-console console.error(`Could not fetch backlink text from any parent, defaulting to "${backLinkLabel}" instead.`, node); } } let listItem = this.extractListItem(node, nextBackLinkLabel); if (listItem !== null) { list.appendChild(listItem); } } }); return list; } static extractListItem(_listItem, backLinkLabel) { let listItem = null; let nodes; if (_listItem.hasAttribute('aria-owns')) { let ownedNodes = _listItem.getAttribute('aria-owns').split(' '); let newList = document.createElement('ul'); ownedNodes.forEach(_ownedNode => { let ownedElement = document.getElementById(_ownedNode); Array.from(ownedElement.childNodes).forEach(_childNode => { newList.appendChild(_childNode.cloneNode(true)); }); }); let newListItem = document.createElement('LI'); newListItem.innerHTML = _listItem.innerHTML; newListItem.appendChild(newList); nodes = newListItem.childNodes; } else { nodes = _listItem.childNodes; } Array.from(nodes).forEach(item => { if (item.nodeName === 'A') { if (listItem === null) { listItem = document.createElement('li'); } let listItemLink = document.createElement('a'); listItemLink.innerText = item.textContent.trim(); listItemLink.setAttribute('href', item.getAttribute('href')); listItem.appendChild(listItemLink); if (_listItem.hasAttribute('data-section-link')) { listItem.classList.add('rsm-section-link'); } } else if (item.nodeName === 'UL' && !item.classList.contains('rsm-ignore')) { if (listItem === null) { listItem = document.createElement('li'); } this.subMenuCount += 1; let subMenuId = `rsm-sub-menu-${this.subMenuCount}`; let listItemList = this.extractList(item, subMenuId, backLinkLabel); listItemList.setAttribute('id', subMenuId); let subMenuOpener = listItem.querySelector('a'); let sectionLinkHref = ''; if (subMenuOpener === null) { subMenuOpener = document.createElement('a'); } else { sectionLinkHref = subMenuOpener.getAttribute('href'); } subMenuOpener.setAttribute('data-href', sectionLinkHref); subMenuOpener.classList.add('rsm-sub-opener'); subMenuOpener.setAttribute('href', `#${subMenuId}`); listItem.appendChild(subMenuOpener); listItem.appendChild(listItemList); let backLink = listItemList.querySelector('.rsm-back-link'); // do not append the section link if there is already an entry with this link let linksToThisSection = []; if (_listItem.hasAttribute('aria-owns')) { _listItem.getAttribute('aria-owns').split(' ').forEach(item => { const link = document.querySelector(`#${item}`).querySelector(`li>[href="${sectionLinkHref}"]`); if (!link) { return; } linksToThisSection.push(link); }); } else { const link = _listItem.querySelector(`[id*="rsm-sub-menu"]>li>[href="${sectionLinkHref}"]`); if (link) { linksToThisSection.push(link); } } if (!linksToThisSection.length) { let sectionLinkItem = document.createElement('LI'); let sectionLink = document.createElement('A'); sectionLinkItem.appendChild(sectionLink); sectionLink.innerText = subMenuOpener.textContent || 'EMPTY'; sectionLink.setAttribute('href', `${sectionLinkHref}`); sectionLinkItem.classList.add('rsm-section-link'); backLink.parentNode.insertBefore(sectionLinkItem, backLink.nextSibling); } } }); if (listItem === null) { // eslint-disable-next-line no-console console.error('Could not create list item for element: ', _listItem); return null; } listItem.classList.add('rsm-default-link'); return listItem; } }