Add v-highlight directive for lazy-loaded syntax highlighting using highlight.js

This commit is contained in:
2026-01-28 19:48:46 +00:00
parent 05b2894cc8
commit 5c338bd622

View File

@@ -0,0 +1,90 @@
/**
* v-highlight directive
* Lazy loads syntax highlighting for code blocks using highlight.js
* Only highlights when element enters viewport (IntersectionObserver)
*/
import hljs from 'highlight.js/lib/core';
import json from 'highlight.js/lib/languages/json';
// Register JSON language
hljs.registerLanguage('json', json);
/**
* Apply syntax highlighting to a code element
* @param {HTMLElement} el - The code element to highlight
* @param {string} theme - Theme name ('github' or 'github-dark')
*/
function applyHighlight(el, theme = 'github-dark') {
if (el.classList.contains('hljs')) {
// Already highlighted
return;
}
try {
hljs.highlightElement(el);
el.classList.add(`hljs-theme-${theme}`);
} catch (error) {
console.error('Highlight.js error:', error);
}
}
/**
* Create IntersectionObserver for lazy highlighting
* @param {HTMLElement} el - Element to observe
* @param {string} theme - Theme to apply
* @returns {IntersectionObserver}
*/
function createHighlightObserver(el, theme) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
applyHighlight(entry.target, theme);
observer.disconnect();
}
});
},
{
rootMargin: '50px', // Start highlighting slightly before entering viewport
threshold: 0.01
}
);
observer.observe(el);
return observer;
}
/**
* v-highlight directive
* Usage: <code class="language-json" v-highlight="theme">...</code>
*/
export const vHighlight = {
mounted(el, binding) {
const theme = binding.value || 'github-dark';
// Store observer on element for cleanup
el._highlightObserver = createHighlightObserver(el, theme);
},
updated(el, binding) {
const newTheme = binding.value || 'github-dark';
const oldTheme = binding.oldValue || 'github-dark';
// If theme changed, re-highlight
if (newTheme !== oldTheme && el.classList.contains('hljs')) {
el.classList.remove(`hljs-theme-${oldTheme}`);
el.classList.add(`hljs-theme-${newTheme}`);
}
},
unmounted(el) {
// Cleanup observer
if (el._highlightObserver) {
el._highlightObserver.disconnect();
delete el._highlightObserver;
}
}
};
export default vHighlight;