cheatsheets/_js/wrapify/index.js

127 lines
2.7 KiB
JavaScript

import matches from 'dom101/matches'
import addClass from 'dom101/add-class'
import { appendMany, nextUntil, before, findChildren, createDiv } from '../helpers/dom'
/**
* Wraps h2 sections into h2-section.
* Wraps h3 sections into h3-section.
*
* @private
*/
export default function wrapify (root) {
// These are your H2 sections. Returns a list of .h2-section nodes.
const sections = wrapifyH2(root)
// For each h2 section, wrap the H3's in them
sections.forEach(section => {
const bodies = findChildren(section, '[data-js-h3-section-list]')
bodies.forEach(body => { wrapifyH3(body) })
})
}
/**
* Wraps h2 sections into h2-section.
* Creates and HTML structure like so:
*
* .h2-section
* h2.
* (title)
* .body.h3-section-list.
* (body goes here)
*
* @private
*/
function wrapifyH2 (root) {
return groupify(root, {
tag: 'h2',
wrapperFn: () => createDiv({ class: 'h2-section' }),
bodyFn: (pivot) => {
if (isExcempted(pivot)) {
return createDiv({
class: 'body'
})
} else {
return createDiv({
class: 'body h3-section-list',
'data-js-h3-section-list': ''
})
}
}
})
}
/**
* Checks if an H2 is excempted from formatting its descendant h3's
*/
function isExcempted (h2) {
return h2.classList.contains('-versus')
}
/**
* Wraps h3 sections into h3-section.
* Creates and HTML structure like so:
*
* .h3-section
* h3.
* (title)
* .body.
* (body goes here)
*
* @private
*/
function wrapifyH3 (root) {
return groupify(root, {
tag: 'h3',
wrapperFn: () => createDiv({ class: 'h3-section' }),
bodyFn: () => createDiv({ class: 'body' })
})
}
/**
* Groups all headings (a `tag` selector) under wrappers like `.h2-section`
* (build by `wrapperFn()`).
* @private
*/
export function groupify (el, { tag, wrapperFn, bodyFn }) {
const first = el.children[0]
let result = []
// Handle the markup before the first h2
if (first && !matches(first, tag)) {
const sibs = nextUntil(first, tag)
result.push(wrap(first, null, [ first, ...sibs ]))
}
// Find all h3's inside it
const children = findChildren(el, tag)
children.forEach(child => {
const sibs = nextUntil(child, tag)
result.push(wrap(child, child, sibs))
})
return result
function wrap (pivot, first, sibs) {
const wrap = wrapperFn()
const pivotClass = pivot.className
if (pivotClass) addClass(wrap, pivotClass)
before(pivot, wrap)
const body = bodyFn(pivot)
if (pivotClass) addClass(body, pivotClass)
appendMany(body, sibs)
if (first) wrap.appendChild(first)
wrap.appendChild(body)
return wrap
}
}