61 lines
1.9 KiB
JavaScript
61 lines
1.9 KiB
JavaScript
/**
|
|
* SourceViewer hook — scrolls to and highlights the selected line.
|
|
*
|
|
* Attached to the `#source-viewer` div via `phx-hook="SourceViewer"`.
|
|
* Reads `data-highlighted-line` to find the target line element and
|
|
* scrolls it into view with a smooth animation.
|
|
* Applies highlight.js syntax highlighting to individual line content elements.
|
|
*/
|
|
import hljs from "highlight.js/lib/core"
|
|
|
|
export const SourceViewer = {
|
|
mounted() {
|
|
this.applySyntaxHighlighting()
|
|
this.scrollToHighlighted()
|
|
},
|
|
|
|
updated() {
|
|
this.applySyntaxHighlighting()
|
|
this.scrollToHighlighted()
|
|
},
|
|
|
|
applySyntaxHighlighting() {
|
|
const container = this.el.querySelector(".sv-lines")
|
|
if (!container || container.querySelector(".hljs-on")) return;
|
|
|
|
// Collect the raw text from all line content spans
|
|
const lineContentEls = container.querySelectorAll(".sv-line-content")
|
|
if (lineContentEls.length === 0) return;
|
|
|
|
const rawText = Array.from(lineContentEls).map(el => el.textContent).join("\n")
|
|
|
|
// Create a temp element for highlight.js to process
|
|
const temp = document.createElement("code")
|
|
temp.textContent = rawText
|
|
hljs.highlightElement(temp)
|
|
|
|
// Split the highlighted HTML back into lines
|
|
// highlight.js innerHTML uses actual newlines between lines of code
|
|
const highlightedLines = temp.innerHTML.split("\n")
|
|
|
|
// Apply each highlighted line to the corresponding .sv-line-content
|
|
lineContentEls.forEach((el, i) => {
|
|
if (highlightedLines[i]) {
|
|
el.innerHTML = highlightedLines[i]
|
|
}
|
|
})
|
|
|
|
// Mark as highlighted so we don't re-apply on subsequent updates
|
|
container.classList.add("hljs", "hljs-on")
|
|
},
|
|
|
|
scrollToHighlighted() {
|
|
const lineNum = this.el.dataset.highlightedLine
|
|
if (lineNum !== undefined && lineNum !== "") {
|
|
const lineEl = document.getElementById(`line-${lineNum}`)
|
|
if (lineEl) {
|
|
lineEl.scrollIntoView({ behavior: "smooth", block: "center" })
|
|
}
|
|
}
|
|
}
|
|
} |