129 lines
3.0 KiB
JavaScript
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('');
|
|
}
|
|
|
|
})(); |