Code block improvements

- Add ids, use ids to link copy button. No need for client-side script or updating the copy button manually for dynamic code snippets.
- Add button to link to code block
- Slight refactor on copy plugin to use the 11ty API properly
This commit is contained in:
Lea Verou
2025-02-14 14:53:18 -05:00
parent 4621094ea1
commit d6a91919e0
6 changed files with 44 additions and 35 deletions

View File

@@ -94,7 +94,7 @@ export default function (eleventyConfig) {
eleventyConfig.addPlugin(highlightCodePlugin());
// Add copy code buttons to code blocks
eleventyConfig.addPlugin(copyCodePlugin());
eleventyConfig.addPlugin(copyCodePlugin);
// Various text replacements
eleventyConfig.addPlugin(

View File

@@ -5,7 +5,6 @@
<meta name="theme-color" content="#f36944">
<script type="module" src="/assets/scripts/code-examples.js"></script>
<script type="module" src="/assets/scripts/copy-code.js"></script>
<script type="module" src="/assets/scripts/scroll.js"></script>
<script type="module" src="/assets/scripts/turbo.js"></script>

View File

@@ -3,30 +3,39 @@ import { parse } from 'node-html-parser';
/**
* Eleventy plugin to add copy buttons to code blocks.
*/
export function copyCodePlugin(options = {}) {
export function copyCodePlugin(eleventyConfig, options = {}) {
options = {
container: 'body',
...options,
};
return function (eleventyConfig) {
eleventyConfig.addTransform('copy-code', content => {
const doc = parse(content, { blockTextElements: { code: true } });
const container = doc.querySelector(options.container);
let codeCount = 0;
eleventyConfig.addTransform('copy-code', content => {
const doc = parse(content, { blockTextElements: { code: true } });
const container = doc.querySelector(options.container);
if (!container) {
return content;
if (!container) {
return content;
}
// Look for code blocks
container.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
let preId = pre.getAttribute('id') || `code-block-${++codeCount}`;
let codeId = code.getAttribute('id') || `${preId}-inner`;
if (!code.getAttribute('id')) {
code.setAttribute('id', codeId);
}
if (!pre.getAttribute('id')) {
pre.setAttribute('id', preId);
}
// Look for code blocks
container.querySelectorAll('pre > code').forEach(code => {
const pre = code.closest('pre');
// Add a copy button (we set the copy data at runtime to reduce page bloat)
pre.innerHTML = `<wa-copy-button class="copy-button" hoist></wa-copy-button>` + pre.innerHTML;
});
return doc.toString();
// Add a copy button
pre.innerHTML += `<wa-icon-button href="#${preId}" class="block-link-icon" name="link"></wa-icon-button>
<wa-copy-button from="${codeId}" class="copy-button" hoist></wa-copy-button>`;
});
};
return doc.toString();
});
}

View File

@@ -1,15 +0,0 @@
function setCopyValue() {
document.querySelectorAll('.copy-button').forEach(copyButton => {
const pre = copyButton.closest('pre');
const code = pre?.querySelector('code');
if (code) {
copyButton.value = code.textContent;
}
});
}
// Set data for all copy buttons when the page loads
setCopyValue();
document.addEventListener('turbo:load', setCopyValue);

View File

@@ -27,3 +27,19 @@ wa-copy-button.copy-button {
opacity: 1;
}
}
.block-link-icon {
position: absolute;
inset-block-start: 0;
inset-inline-end: calc(100% + var(--wa-space-s));
transition: var(--wa-transition-slow);
&:not(:hover, :focus) {
opacity: 50%;
}
:not(:hover, :focus-within) > & {
opacity: 0;
}
}

View File

@@ -9,7 +9,7 @@
--banner-height: 0px;
--header-height: 0px;
--subheader-height: 0px;
--scroll-margin-top: calc(var(--header-height, 0px) + var(--subheader-height, 0px));
--scroll-margin-top: calc(var(--header-height, 0px) + var(--subheader-height, 0px) + 0.5em);
}
slot[name]:not([name='skip-to-content'], [name='navigation-toggle'])::slotted(*) {