diff --git a/.editorconfig b/.editorconfig index a384dc4..17feeda 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,11 @@ [*.html] indent_style = "space" -indent_size = 4 \ No newline at end of file +indent_size = 4 + +[*.scss] +indent_style = "space" +indent_size = 4 + +[*.js] +indent_style = "space" +indent_size = 2 \ No newline at end of file diff --git a/assets/sass/researcher.scss b/assets/sass/researcher.scss index 2066192..f708d4d 100644 --- a/assets/sass/researcher.scss +++ b/assets/sass/researcher.scss @@ -1,6 +1,7 @@ // Sizes $max-width: {{ .Param "style.pageWidth" | default "750px;" }}; $avatar-size: {{ .Param "style.avatarSize" | default "90px;" }}; +$toc-width: {{ .Param "style.tocWidth" | default "350px;" }}; // Colors $black: {{ .Param "style.colorBlack" | default "#222222" }}; @@ -48,9 +49,105 @@ $y-medium: 1.0rem; font-family: $font-family; line-height: 1.2; } + +$toc-left-width: $toc-width + $max-width + 25px; + +.root { + display: flex; + flex-direction: column; +} +.table-of-contents { + order: 0; + ul { + padding-left: 1.0rem !important; + & > li { + margin-left: 0.3em !important; + } + } + + a.active { + font-weight: bold; + } + + a:hover { + cursor: pointer; + } + + @media(max-width: 578px) { + align-self: center; + } +} .container { max-width: $max-width; + order: 1 } +#title-large-screen { + display: none; +} +#title-small-screen { + margin-left: 15px !important; + + @media(max-width: 578px) { + align-self: center; + } +} + +@media (max-width: $toc-left-width) { + .root { + margin-right: auto; + margin-left: auto; + width: 100%; + max-width: $max-width; + } + + .table-of-contents { + padding-left: 15px; + padding-right: 15px; + } +} + +@media (min-width: $toc-width * 1.5 + $max-width) { + .root { + margin-left: calc((100vw - 750px) / 2); + } + #actual-content { + margin: 0; + } +} + +@media(min-width: $toc-left-width) { + .root { + flex-direction: row; + } + + .table-of-contents { + width: $toc-width; + order: 2; + position: sticky; + top: 0px; + padding: 1em; + align-self: start; + scrollbar-width: thin; + + .table-of-contents-text { + overflow-x: hidden; + overflow-y: auto; + max-height: calc(100vh - 155px); + } + + .hidden { + display: none; + } + } + + #title-small-screen { + display: none; + } + #title-large-screen { + display: block; + } +} + .navbar-brand { @extend %link-dark; font-size: 2rem; diff --git a/config.toml b/config.toml index 020e453..3614ddc 100644 --- a/config.toml +++ b/config.toml @@ -45,6 +45,8 @@ staticDir = ["static"] noHl = false style = 'pygments' tabWidth = 4 + [markup.tableOfContents] + endLevel = 4 [markup.goldmark.renderer] unsafe = true # allow raw HTML in markdown files \ No newline at end of file diff --git a/layouts/_default/single.html b/layouts/_default/single.html index 140759a..443deaa 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -1,6 +1,18 @@ {{ define "main" }} -
-

{{ .Title }}

- {{ .Content }} + +
+

{{ .Title }}

+
+

{{ .Title }}

+ {{ .Content }} +
+
+
+ Table of Contents + {{ .TableOfContents }} +
+ + +
{{ end }} diff --git a/scripts/publish-configs.el b/scripts/publish-configs.el index bf71c9c..42453e9 100644 --- a/scripts/publish-configs.el +++ b/scripts/publish-configs.el @@ -31,6 +31,9 @@ :ensure t) (setq org-make-toc-link-type-fn #'org-make-toc--link-entry-org) +(setq org-hugo-anchor-functions '(org-hugo-get-page-or-bundle-name + org-hugo-get-custom-id + org-hugo-get-md5)) (setq org-hugo-section "configs") (setq org-hugo-base-dir (vc-find-root default-directory ".git")) @@ -52,7 +55,11 @@ file)) file 'overwrite)) -;; (copy-directory (expand-file-name "~/dot-stats/img") "dot-stats/img" t t) +(copy-directory + (expand-file-name + (format "%s/repos/dotfiles/dot-imgs/" + (vc-find-root default-directory ".git"))) + "dot-imgs" t t) (dolist (file my/config-files) (with-temp-buffer diff --git a/static/js/dynamic-toc.js b/static/js/dynamic-toc.js new file mode 100644 index 0000000..2292873 --- /dev/null +++ b/static/js/dynamic-toc.js @@ -0,0 +1,121 @@ +const tocId = "TableOfContents"; +const actualContentId = "actual-content"; +let showAll = false; +let currentActiveLinkId = null; +let elemsToHide = []; +let linksById = {}; + +let headerObserver = null; + +function observeHeadings() { + const links = document.querySelectorAll(`#${tocId} a`); + const headings = document.querySelectorAll(`${actualContentId} h1,h2,h3,h4`); + + for (const link of links) { + linksById[link.getAttribute("href")] = link; + } + for (const elem of document.querySelectorAll(`#${tocId} ul`)) { + if (elem.parentElement.id !== tocId) { + elemsToHide.push(elem); + } + } + + headerObserver = new IntersectionObserver( + (entries) => { + for (const entry of entries) { + if (entry.isIntersecting && linksById[`#${entry.target.id}`]) { + currentActiveLinkId = `#${entry.target.id}`; + break; + } + } + if (currentActiveLinkId) { + for (const link of links) { + link.classList.remove("active"); + } + linksById[currentActiveLinkId].classList.add("active"); + + if (!showAll) { + hideHeadings(); + } + } + }, + { + threshold: 0.1, + root: document.querySelector(`${actualContentId}`), + } + ); + + for (const heading of headings) { + headerObserver.observe(heading); + } + hideHeadings(); +} + +function observeButtons() { + const unhideButton = document.getElementById("unhide-all-button"); + const hideButton = document.getElementById("hide-all-button"); + unhideButton.addEventListener("click", () => { + showAll = true; + showHeadings(); + unhideButton.classList.add("hidden"); + hideButton.classList.remove("hidden"); + }); + hideButton.addEventListener("click", () => { + showAll = false; + hideHeadings(); + hideButton.classList.add("hidden"); + unhideButton.classList.remove("hidden"); + }); + unhideButton.classList.remove("hidden"); +} + +function hideHeadings() { + for (const elem of elemsToHide) { + elem.classList.add("hidden"); + } + if (!currentActiveLinkId) { + return; + } + for ( + let elem = linksById[currentActiveLinkId]; + (elem = elem.parentElement); + elem.id !== tocId + ) { + elem.classList.remove("hidden"); + } + for (const elem of linksById[currentActiveLinkId].parentElement.children) { + elem.classList.remove("hidden"); + } +} + +function showHeadings() { + for (const elem of elemsToHide) { + elem.classList.remove("hidden"); + } +} + +function setUpObserver() { + if (document.documentElement.clientWidth >= (750 + 350 + 25)) { + if (headerObserver === null) { + observeHeadings(); + observeButtons(); + } + } else { + if (headerObserver !== null) { + headerObserver.disconnect(); + headerObserver = null; + showHeadings(); + } + } +} + +window.addEventListener("load", (event) => { + if ("IntersectionObserver" in window) { + setUpObserver(); + window.addEventListener("resize", setUpObserver); + } +}); + +window.addEventListener("unload", (event) => { + headerObserver.disconnect(); +});