Skip to content
File

Blob: src/client/islands/code-line-anchors.ts

typescript73 lines
1/// <reference lib="dom" />
2 
3function getTargetRow(rowId: string): HTMLElement | null {
4 const row = document.getElementById(rowId);
5 if (!row || !row.classList.contains("code-line")) {
6 return null;
7 }
8 
9 return row;
10}
11 
12function clearCurrentHighlight() {
13 document.querySelector("#blob-code .line-highlight")?.classList.remove("line-highlight");
14}
15 
16function highlightRow(rowId: string, smooth: boolean) {
17 const row = getTargetRow(rowId);
18 if (!row) {
19 clearCurrentHighlight();
20 return;
21 }
22 
23 clearCurrentHighlight();
24 row.classList.add("line-highlight");
25 row.scrollIntoView({ block: "center", behavior: smooth ? "smooth" : "auto" });
26}
27 
28function highlightCurrentHash(smooth: boolean) {
29 const hash = window.location.hash;
30 if (!/^#L\d+$/.test(hash)) {
31 clearCurrentHighlight();
32 return;
33 }
34 
35 highlightRow(hash.slice(1), smooth);
36}
37 
38export function initCodeLineAnchors() {
39 if (!document.getElementById("blob-code")) {
40 return;
41 }
42 
43 document.addEventListener("click", (event) => {
44 const target = event.target as HTMLElement;
45 const link = target.closest<HTMLAnchorElement>('#blob-code .line-num a[href^="#L"]');
46 const href = link?.getAttribute("href") || "";
47 if (!link || !/^#L\d+$/.test(href)) {
48 return;
49 }
50 
51 const rowId = href.slice(1);
52 if (!getTargetRow(rowId)) {
53 return;
54 }
55 
56 event.preventDefault();
57 const nextHash = `#${rowId}`;
58 if (window.location.hash === nextHash) {
59 highlightRow(rowId, true);
60 return;
61 }
62 
63 history.pushState(null, "", nextHash);
64 highlightRow(rowId, true);
65 });
66 
67 window.addEventListener("hashchange", () => {
68 highlightCurrentHash(true);
69 });
70 
71 highlightCurrentHash(false);
72}