diff options
| author | Kyren223 <contact@kyren.codes> | 2026-10-09 19:11:34 +0300 |
|---|---|---|
| committer | Kyren223 <contact@kyren.codes> | 2026-10-09 19:11:34 +0300 |
| commit | 660e6fb1b4b4fa57596471d80c4e4264c711f915 (patch) | |
| tree | 6dc9f7329d9c4e724e681d8550105d794e6e90ff /public/footnotes.js | |
| parent | 66a5c4ea1793886aad80a67ad0e40aca6066bcde (diff) | |
Blog stuff initial version
Diffstat (limited to 'public/footnotes.js')
| -rw-r--r-- | public/footnotes.js | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/public/footnotes.js b/public/footnotes.js new file mode 100644 index 0000000..50f709e --- /dev/null +++ b/public/footnotes.js @@ -0,0 +1,69 @@ +document.addEventListener("DOMContentLoaded", () => { + const area = document.querySelector("[data-scroll-area]"); + const refs = [...document.querySelectorAll("a.footnote-ref")]; + if (!area || refs.length === 0) return; + + const panel = document.createElement("div"); + panel.className = "sidenotes"; + document.body.append(panel); + + const items = refs.map((ref) => { + ref.addEventListener("click", (e) => e.preventDefault()); + + const note = document.getElementById(ref.getAttribute("href").slice(1)); + const num = document.createElement("span"); + num.className = "sidenote-num"; + num.textContent = ref.textContent; + + const body = document.createElement("div"); + body.append(...note.cloneNode(true).childNodes); + body.querySelector(".footnote-backref")?.remove(); + + const el = document.createElement("div"); + el.className = "sidenote"; + el.append(num, body); + panel.append(el); + return { ref, el }; + }); + + const gap = 6; + let queued = false; + + const layout = () => { + queued = false; + const rect = area.getBoundingClientRect(); + const frame = document.querySelector(".terminal").getBoundingClientRect(); + const rem = parseFloat(getComputedStyle(document.documentElement).fontSize); + const left = frame.right + rem; + + Object.assign(panel.style, { + left: `${left}px`, + top: `${rect.top}px`, + width: `${window.innerWidth - left - rem}px`, + height: `${rect.height}px`, + }); + + let bottom = -Infinity; + for (const { ref, el } of items) { + const top = Math.max( + ref.getBoundingClientRect().top - rect.top, + bottom + gap, + ); + el.style.top = `${top}px`; + bottom = top + el.offsetHeight; + } + }; + + const schedule = () => { + if (!queued) { + queued = true; + requestAnimationFrame(layout); + } + }; + + area.addEventListener("scroll", schedule, { passive: true }); + window.addEventListener("resize", schedule); + new ResizeObserver(schedule).observe(area); + document.fonts?.ready.then(schedule); + layout(); +}); |
