UNPKG

enketo-core

Version:

Extensible Enketo form engine

187 lines (170 loc) 6.32 kB
/** * Table of Contents (toc) module. * * @module toc */ import { getAncestors, getSiblingElement } from './dom-utils'; export default { /** * @type {Array} * @default */ tocItems: [], /** * @type {number} * @default */ _maxTocLevel: [], /** * Generate ToC Items */ generateTocItems() { this.tocItems = []; const tocElements = [ ...this.form.view.$[0].querySelectorAll( '.question:not([role="comment"]), .or-group' ), ] .filter( (tocEl) => !tocEl.closest('.disabled') && (tocEl.matches('.question') || tocEl.querySelector('.question:not(.disabled)') || // or-repeat-info is only considered a page by itself if it has no sibling repeats // When there are siblings repeats, we use CSS trickery to show the + button underneath the last // repeat. (tocEl.matches('.or-repeat-info') && !getSiblingElement(tocEl, '.or-repeat'))) ) .filter((tocEl) => !tocEl.classList.contains('or-repeat-info')); tocElements.forEach((element, index) => { const groupParents = getAncestors(element, '.or-group'); this.tocItems.push({ element, level: groupParents.length, parent: groupParents.length > 0 ? groupParents[groupParents.length - 1] : null, tocId: index, tocParentId: null, }); }); this._maxTocLevel = Math.max(...this.tocItems.map((el) => el.level)); const newTocParents = this.tocItems.filter( (item) => item.level < this._maxTocLevel && item.element.classList.contains('or-group') ); this.tocItems.forEach((item) => { const parentItem = newTocParents.find( (parent) => item.parent === parent.element ); if (parentItem) { item.tocParentId = parentItem.tocId; } }); }, /** * Generate ToC Html Fragment * * @return {DocumentFragment} HTML list element containing Table of Contents */ getHtmlFragment() { this.generateTocItems(); const toc = document.createDocumentFragment(); let currentTocLevel = 0; do { const currentTocLevelItems = this.tocItems.filter( (item) => item.level === currentTocLevel ); if (currentTocLevel === 0) { this._buildTocHtmlList(currentTocLevelItems, toc); } else { const currentLevelParentIds = [ ...new Set( currentTocLevelItems.map((item) => item.tocParentId) ), ]; currentLevelParentIds.forEach((parentId) => { const tocList = document.createElement('ul'); const currentLTocevelItemsWithSameIds = currentTocLevelItems.filter( (item) => item.tocParentId === parentId ); this._buildTocHtmlList( currentLTocevelItemsWithSameIds, tocList ); const tocParent = toc.querySelectorAll( `[tocId="${parentId}"]` )[0]; tocParent.appendChild(tocList); }); } currentTocLevel++; } while (currentTocLevel <= this._maxTocLevel); return toc; }, /** * Get Title of Current ToC Element * * @param {Element} el - HTML element that serves as page */ _getTitle(el) { let tocItemText; const labelEl = el.querySelector('.question-label.active'); if (labelEl) { tocItemText = labelEl.textContent; } else { const hintEl = el.querySelector('.or-hint.active'); if (hintEl) { tocItemText = hintEl.textContent; } } tocItemText = tocItemText && tocItemText.length > 20 ? `${tocItemText.substring(0, 20)}...` : tocItemText; return tocItemText; }, /** * Builds List of ToC Items * * @param {Array<object>} items - ToC list of items * @param {Element} appendTo - HTML Element to append ToC list to */ _buildTocHtmlList(items, appendTo) { if (items.length > 0) { items.forEach((item) => { const tocListItem = document.createElement('li'); if (item.element.classList.contains('or-group')) { const groupTocTitle = document.createElement('summary'); groupTocTitle.textContent = this._getTitle(item.element) || `[${item.tocId + 1}]`; const groupToc = document.createElement('details'); groupToc.setAttribute('tocId', item.tocId); if (item.tocParentId !== null) { groupToc.setAttribute('tocParentId', item.tocParentId); } groupToc.append(groupTocTitle); tocListItem.append(groupToc); } else { const a = document.createElement('a'); a.textContent = this._getTitle(item.element) || `[${item.tocId + 1}]`; tocListItem.setAttribute('tocId', item.tocId); tocListItem.setAttribute('role', 'pageLink'); if (item.tocParentId !== null) { tocListItem.setAttribute( 'tocParentId', item.tocParentId ); } tocListItem.append(a); } appendTo.append(tocListItem); }); } }, };