@sgalinski/responsive-side-menu
Version:
A simple and extendable slide-in menu
149 lines (142 loc) • 5.45 kB
JavaScript
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;
}
}