docs2website/extras/components/outline-button.js

129 lines
3.0 KiB
JavaScript

(() => {
const size = 1.25;
const outline_attr = 'data-show-outline';
const styles = `
:host {
display: contents;
}
:host .wrapper {
width: ${size}rem;
height: ${size}rem;
padding: 0.5rem;
border-radius: 100%;
cursor: pointer;
overflow: hidden;
position: relative;
}
:host svg.icon {
--icon-size: ${size}rem;
color: var(--theme-text-body);
position: absolute;
top: 0.5rem;
left: 0.5rem;
pointer-events: none;
transition: color linear .5s;
}
`;
const template = `
<style>${styles}</style>
<div class="wrapper" title="Toggle Outline" tabindex="0" role="button" aria-pressed="false">
{{{ icons.list }}}
</div>
<nav id="outline-panel" aria-hidden="true">
<!-- todo: i18n -->
<h2>Outline</h2>
</nav>
`;
customElements.define('outline-button',
class OutlineButton extends HTMLElement {
#wrapper = null;
#outline = null;
#outline_built = false;
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = template;
this.#wrapper = this.shadowRoot.querySelector('.wrapper');
this.#outline = this.shadowRoot.querySelector('#outline-panel');
this.#outline.parentNode.removeChild(this.#outline);
document.body.appendChild(this.#outline);
}
connectedCallback() {
this.addEventListener('click', this.onSelect);
this.addEventListener('keydown', this.onKeydown);
this.addEventListener('keyup', this.onKeyup);
}
disconnectedCallback() {
this.removeEventListener('click', this.onSelect);
this.removeEventListener('keydown', this.onKeydown);
this.removeEventListener('keyup', this.onKeyup);
}
onKeydown = (/** @type KeyboardEvent */ event) => {
if (event.keyCode === 32) {
event.preventDefault();
}
if (event.keyCode === 13) {
event.preventDefault();
this.onSelect();
}
};
onKeyup = (/** @type KeyboardEvent */ event) => {
if (event.keyCode === 32) {
event.preventDefault();
this.onSelect();
}
};
onSelect = () => {
if (! this.#outline_built) {
const root_selector = this.getAttribute('content-root');
this.#outline.innerHTML += `<ol>${build_outline(root_selector)}</ol>`;
this.#outline_built = true;
}
if (document.body.hasAttribute(outline_attr)) {
document.body.removeAttribute(outline_attr);
this.#outline.setAttribute('aria-hidden', 'true');
this.#wrapper.setAttribute('aria-pressed', 'false');
}
else {
document.body.setAttribute(outline_attr, '');
this.#outline.removeAttribute('aria-hidden');
this.#wrapper.setAttribute('aria-pressed', 'true');
}
};
}
);
function build_outline(root_selector) {
const root = document.querySelector(root_selector);
const headings = root.querySelectorAll('h1, h2, h3, h4, h5, h6');
const outline = [ ];
for (const heading of headings) {
outline.push(`
<li data-depth="${heading.tagName.toLowerCase()}">
<a href="#${heading.id}">${heading.innerText}</a>
</li>
`);
}
return outline.join('');
}
})();