96 lines
2.6 KiB
JavaScript
96 lines
2.6 KiB
JavaScript
/**
|
|
* 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);
|
|
// Remove any existing theme classes
|
|
el.className = el.className.replace(/hljs-theme-\w+/, '');
|
|
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 v-highlight="{ theme: 'github', language: 'json' }">...</code>
|
|
*/
|
|
export const vHighlight = {
|
|
mounted(el, binding) {
|
|
const config = binding.value || {};
|
|
const theme = typeof config === 'string' ? config : (config.theme || 'github-dark');
|
|
|
|
// Store observer on element for cleanup
|
|
el._highlightObserver = createHighlightObserver(el, theme);
|
|
},
|
|
|
|
updated(el, binding) {
|
|
const newConfig = binding.value || {};
|
|
const newTheme = typeof newConfig === 'string' ? newConfig : (newConfig.theme || 'github-dark');
|
|
const oldConfig = binding.oldValue || {};
|
|
const oldTheme = typeof oldConfig === 'string' ? oldConfig : (oldConfig.theme || '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;
|