✨ Add v-highlight directive for lazy-loaded syntax highlighting using highlight.js
This commit is contained in:
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user