Files
webawesome/docs/_utils/code-examples.js

186 lines
5.3 KiB
JavaScript
Raw Normal View History

2024-04-17 11:20:27 -04:00
import { parse } from 'node-html-parser';
import { v4 as uuid } from 'uuid';
const templates = {
old(pre, code, { open, buttons, edit }) {
const id = `code-example-${uuid().slice(-12)}`;
let preview = code.textContent;
// Run preview scripts as modules to prevent collisions
const root = parse(preview, { blockTextElements: { script: true } });
root.querySelectorAll('script').forEach(script => script.setAttribute('type', 'module'));
preview = root.toString();
return `
<div class="code-example ${open ? 'open' : ''}">
<div class="code-example-preview">
${preview}
</div>
<div class="code-example-source" id="${id}">
${pre.outerHTML}
</div>
${
buttons
? `
<div class="code-example-buttons">
<button
class="code-example-toggle"
type="button"
aria-expanded="${open ? 'true' : 'false'}"
aria-controls="${id}"
>
Code
<wa-icon name="chevron-down"></wa-icon>
</button>
${
edit
? `
<button class="code-example-pen" type="button">
<wa-icon name="pen-to-square"></wa-icon>
Edit
</button>
`
: ''
}
`
: ''
}
</div>
</div>
`;
},
new(pre, code, { open, first, attributes }) {
attributes = {
open,
include: `link[rel=stylesheet][href^='/dist/']`,
...attributes,
};
const attributesString = Object.entries(attributes)
.map(([key, value]) => {
if (value === true) {
return key;
}
if (value === false || value === null) {
return '';
}
return `${key}="${value}"`;
})
.join(' ');
let includes = '';
if (first) {
includes = `
<template class="wa-code-demo-include-isolated">
2024-12-09 19:40:28 -05:00
<script src="/dist/webawesome.loader.js" type="module"></script>
</template>`;
}
let preview = '';
2024-12-09 20:43:58 -05:00
if (attributes.viewport === undefined) {
// Slot in pre-rendered preview
2024-12-09 20:43:58 -05:00
preview = `<div style="display:contents" slot="preview">${code.textContent}</div>`;
// Run preview scripts as modules to prevent collisions
const root = parse(preview, { blockTextElements: { script: true } });
root.querySelectorAll('script').forEach(script => script.setAttribute('type', 'module'));
preview = root.toString();
2024-12-09 20:43:58 -05:00
}
return `${includes}
<wa-code-demo ${attributesString}>
${preview}
${pre.outerHTML}
</wa-code-demo>
`;
},
};
2024-04-17 11:20:27 -04:00
/**
* Eleventy plugin to turn `<code class="example">` blocks into live examples.
*/
export function codeExamplesPlugin(eleventyConfig, options = {}) {
const defaultOptions = {
2024-04-17 11:20:27 -04:00
container: 'body',
defaultOpen: () => false,
2024-04-17 11:20:27 -04:00
};
options = { ...defaultOptions, ...options };
2024-04-17 11:20:27 -04:00
const stats = {
inputPaths: {},
outputPaths: {},
};
eleventyConfig.addTransform('code-examples', function (content) {
const { inputPath, outputPath } = this.page;
const doc = parse(content, { blockTextElements: { code: true } });
const container = doc.querySelector(options.container);
2024-04-17 11:20:27 -04:00
if (!container) {
return content;
}
// Look for external links
container.querySelectorAll('code.example').forEach(code => {
stats.inputPaths[inputPath] ??= 0;
stats.outputPaths[outputPath] ??= 0;
stats.inputPaths[inputPath]++;
stats.outputPaths[outputPath]++;
const pre = code.closest('pre');
const first = stats.inputPaths[inputPath] === 1;
const localOptions = {
...options,
first,
// Modifier defaults
edit: true,
buttons: true,
new: true, // comment this line to default back to the old demos
attributes: {},
};
for (const prop of ['new', 'open', 'buttons', 'edit']) {
if (code.classList.contains(prop)) {
localOptions[prop] = true;
} else if (code.classList.contains(`no-${prop}`)) {
localOptions[prop] = false;
}
2024-04-17 11:20:27 -04:00
}
for (const attribute of ['viewport', 'include']) {
if (code.hasAttribute(attribute)) {
localOptions.attributes[attribute] = code.getAttribute(attribute);
code.removeAttribute(attribute);
}
}
if (Object.keys(localOptions.attributes).length > 0) {
// attributes only work on the new syntax
localOptions.new = true;
}
if (localOptions.open === undefined) {
if (localOptions.defaultOpen === true) {
localOptions.open = localOptions.defaultOpen;
} else if (typeof localOptions.defaultOpen === 'function') {
localOptions.open = localOptions.defaultOpen(code, {
pre,
inputPathIndex: stats.inputPaths[inputPath],
outputPathIndex: stats.outputPaths[outputPath],
});
}
}
const template = localOptions.new ? 'new' : 'old';
const codeExample = parse(templates[template](pre, code, localOptions));
pre.replaceWith(codeExample);
2024-04-17 11:20:27 -04:00
});
return doc.toString();
});
2024-04-17 11:20:27 -04:00
}