125 lines
2.9 KiB
JavaScript
125 lines
2.9 KiB
JavaScript
/**
|
|
* TOC button, topbar and popup for mobile devices
|
|
*/
|
|
|
|
const $tocBar = document.getElementById('toc-bar');
|
|
const $soloTrigger = document.getElementById('toc-solo-trigger');
|
|
const $triggers = document.getElementsByClassName('toc-trigger');
|
|
const $popup = document.getElementById('toc-popup');
|
|
const $btnClose = document.getElementById('toc-popup-close');
|
|
|
|
const SCROLL_LOCK = 'overflow-hidden';
|
|
const CLOSING = 'closing';
|
|
|
|
export class TocMobile {
|
|
static #invisible = true;
|
|
static #barHeight = 16 * 3; // 3rem
|
|
|
|
static options = {
|
|
tocSelector: '#toc-popup-content',
|
|
contentSelector: '.content',
|
|
ignoreSelector: '[data-toc-skip]',
|
|
headingSelector: 'h2, h3, h4',
|
|
orderedList: false,
|
|
scrollSmooth: false,
|
|
collapseDepth: 4,
|
|
headingsOffset: this.#barHeight
|
|
};
|
|
|
|
static initBar() {
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((entry) => {
|
|
$tocBar.classList.toggle('invisible', entry.isIntersecting);
|
|
});
|
|
},
|
|
{ rootMargin: `-${this.#barHeight}px 0px 0px 0px` }
|
|
);
|
|
|
|
observer.observe($soloTrigger);
|
|
this.#invisible = false;
|
|
}
|
|
|
|
static listenAnchors() {
|
|
const $anchors = document.getElementsByClassName('toc-link');
|
|
[...$anchors].forEach((anchor) => {
|
|
anchor.onclick = () => this.hidePopup();
|
|
});
|
|
}
|
|
|
|
static refresh() {
|
|
if (this.#invisible) {
|
|
this.initComponents();
|
|
}
|
|
tocbot.refresh(this.options);
|
|
this.listenAnchors();
|
|
}
|
|
|
|
static get popupOpened() {
|
|
return $popup.open;
|
|
}
|
|
|
|
static showPopup() {
|
|
this.lockScroll(true);
|
|
$popup.showModal();
|
|
const activeItem = $popup.querySelector('li.is-active-li');
|
|
activeItem.scrollIntoView({ block: 'center' });
|
|
}
|
|
|
|
static hidePopup() {
|
|
$popup.toggleAttribute(CLOSING);
|
|
|
|
$popup.addEventListener(
|
|
'animationend',
|
|
() => {
|
|
$popup.toggleAttribute(CLOSING);
|
|
$popup.close();
|
|
},
|
|
{ once: true }
|
|
);
|
|
|
|
this.lockScroll(false);
|
|
}
|
|
|
|
static lockScroll(enable) {
|
|
document.documentElement.classList.toggle(SCROLL_LOCK, enable);
|
|
document.body.classList.toggle(SCROLL_LOCK, enable);
|
|
}
|
|
|
|
static clickBackdrop(event) {
|
|
if ($popup.hasAttribute(CLOSING)) {
|
|
return;
|
|
}
|
|
|
|
const rect = event.target.getBoundingClientRect();
|
|
if (
|
|
event.clientX < rect.left ||
|
|
event.clientX > rect.right ||
|
|
event.clientY < rect.top ||
|
|
event.clientY > rect.bottom
|
|
) {
|
|
this.hidePopup();
|
|
}
|
|
}
|
|
|
|
static initComponents() {
|
|
this.initBar();
|
|
|
|
[...$triggers].forEach((trigger) => {
|
|
trigger.onclick = () => this.showPopup();
|
|
});
|
|
|
|
$popup.onclick = (e) => this.clickBackdrop(e);
|
|
$btnClose.onclick = () => this.hidePopup();
|
|
$popup.oncancel = (e) => {
|
|
e.preventDefault();
|
|
this.hidePopup();
|
|
};
|
|
}
|
|
|
|
static init() {
|
|
tocbot.init(this.options);
|
|
this.listenAnchors();
|
|
this.initComponents();
|
|
}
|
|
}
|