mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 20:19:13 +00:00
* Fix outline for headings that have links Previously produced blank items because it assumed any link in a heading is an anchor * Filter unlisted items from overviews Previously they were filtered only when the card was rendered, so their heading was still shown * [Overview] Add id to group headings * Hide headings from empty groups Should never happen but you never know * [Overview] Ensure "Other" is always last even when no sorting
70 lines
2.3 KiB
JavaScript
70 lines
2.3 KiB
JavaScript
import { parse } from 'node-html-parser';
|
|
|
|
/**
|
|
* Eleventy plugin to add an outline (table of contents) to the page. Headings must have an id, otherwise they won't be
|
|
* included in the outline. An unordered list containing links will be appended to the target element.
|
|
*
|
|
* If no headings are found for the outline, the `ifEmpty()` function will be called with a `node-html-parser` object as
|
|
* the first argument. This can be used to toggle classes or remove elements when the outline is empty.
|
|
*
|
|
* See the `node-html-parser` docs for more details: https://www.npmjs.com/package/node-html-parser
|
|
*/
|
|
export function outlinePlugin(options = {}) {
|
|
options = {
|
|
container: 'body',
|
|
target: '.outline',
|
|
selector: 'h2,h3',
|
|
ifEmpty: () => null,
|
|
...options,
|
|
};
|
|
|
|
return function (eleventyConfig) {
|
|
eleventyConfig.addTransform('outline', content => {
|
|
const doc = parse(content);
|
|
const container = doc.querySelector(options.container);
|
|
const ul = parse('<ul></ul>');
|
|
let numLinks = 0;
|
|
|
|
if (!container) {
|
|
return content;
|
|
}
|
|
|
|
container.querySelectorAll(options.selector).forEach(heading => {
|
|
const id = heading.getAttribute('id');
|
|
const level = heading.tagName.slice(1);
|
|
const clone = parse(heading.outerHTML);
|
|
|
|
if (heading.closest('[data-no-outline]')) {
|
|
return;
|
|
}
|
|
|
|
// Create a clone of the heading so we can remove links and [data-no-outline] elements from the text content
|
|
clone.querySelectorAll('.wa-visually-hidden, [hidden], [aria-hidden="true"]').forEach(el => el.remove());
|
|
clone.querySelectorAll('[data-no-outline]').forEach(el => el.remove());
|
|
|
|
// Generate the link
|
|
const li = parse(`<li data-level="${level}"><a></a></li>`);
|
|
const a = li.querySelector('a');
|
|
a.setAttribute('href', `#${encodeURIComponent(id)}`);
|
|
a.textContent = clone.textContent.trim().replace(/#$/, '');
|
|
|
|
// Add it to the list
|
|
ul.firstChild.appendChild(li);
|
|
numLinks++;
|
|
});
|
|
|
|
if (numLinks > 0) {
|
|
// Append the list to all matching targets
|
|
doc.querySelectorAll(options.target).forEach(target => {
|
|
target.appendChild(parse(ul.outerHTML));
|
|
});
|
|
} else {
|
|
// Remove if empty
|
|
options.ifEmpty(doc);
|
|
}
|
|
|
|
return doc.toString();
|
|
});
|
|
};
|
|
}
|