From c95051b93d756d5653ebe36a8f5f2d556b3843a0 Mon Sep 17 00:00:00 2001 From: lberki Date: Thu, 5 May 2022 11:08:42 +0200 Subject: Add table of contents Add table of contents See #64 --- static/js/toc.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 static/js/toc.js (limited to 'static') diff --git a/static/js/toc.js b/static/js/toc.js new file mode 100644 index 0000000..7229c04 --- /dev/null +++ b/static/js/toc.js @@ -0,0 +1,63 @@ +"use strict"; + +let oldWidth = -1, oldHeight = -1; + +function measureToc() { + if (window.innerWidth === oldWidth && window.innerHeight === oldHeight) { + // In addition to being a bit of optimization, this clause somehow prevents triggering a + // bug in Firefox 98.2.0 on Android that makes the stickiness of the table of contents + // wonky after scrolling to the bottom, then scrolling up. + return; + } + + oldWidth = window.innerWidth; + oldHeight = window.innerHeight; + + let tocContainer = document.getElementById("tocContainer"); + let tocTitle = document.getElementById("tocTitle"); + let tocCollapsible = document.getElementById("tocCollapsible"); + + // Set relevant elements to automatic sizing. + tocContainer.style.width = "fit-content"; + tocCollapsible.style.width = "fit-content"; + tocCollapsible.style.height = "fit-content"; + + // Apparently, this call is necessary so that the CSS properties set above take effect. + requestAnimationFrame(() => { + // These properties apparently round to the nearest integer but rounding down would make + // them wrap text differently than when autosizing. Add a pixel because it's better to wrap + // too little than to wrap too much, + let titleWidth = tocTitle.offsetWidth + 1; + let collapsibleWidth = tocCollapsible.offsetWidth + 1; + let collapsibleHeight = tocCollapsible.offsetHeight + 1; + + // Make sure the TOC width cannot shrink under that of the title. Sadly, + // calc() does not have min() / max() operators. + let clampedCollapsibleWidth = Math.max(collapsibleWidth, titleWidth); + + tocContainer.style.setProperty("--measured-title-width", titleWidth + "px"); + tocContainer.style.setProperty("--measured-expanded-width", clampedCollapsibleWidth + "px"); + tocCollapsible.style.setProperty("--measured-expanded-width", clampedCollapsibleWidth + "px"); + tocCollapsible.style.setProperty("--measured-height", collapsibleHeight + "px"); + + tocContainer.style.removeProperty("width"); + tocCollapsible.style.removeProperty("width"); + tocCollapsible.style.removeProperty("height"); + }); +} + +let resizeTimeout = null; + +function measureTocAfterResize() { + // Chrome sometimes does not finish layout when the resize event handler is called, so wait a + // bit before recalculating sizes. This does not usually result in weird visual effects because + // it's really hard to resize a window while hovering over the table of contents so we can + // assume that it's closed when the measurement is running. + if (resizeTimeout != null) { + clearTimeout(resizeTimeout); + } + resizeTimeout = setTimeout(measureToc, 200); +} + +window.addEventListener("load", measureToc); +window.addEventListener("resize", measureTocAfterResize); -- cgit v1.2.3