From 598050748cb0d3f3dd6ab189e0a93b3ed76ac860 Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Sun, 26 Jun 2022 19:36:31 +0300 Subject: [PATCH 1/4] feat(toc): first version --- .editorconfig | 4 ++ assets/sass/researcher.scss | 86 ++++++++++++++++++++++++++++++++++++ layouts/_default/single.html | 15 +++++-- 3 files changed, 102 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index a384dc4..dfa53db 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,3 +1,7 @@ [*.html] indent_style = "space" +indent_size = 4 + +[*.scss] +indent_style = "space" indent_size = 4 \ No newline at end of file diff --git a/assets/sass/researcher.scss b/assets/sass/researcher.scss index 2066192..1ffa21c 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,94 @@ $y-medium: 1.0rem; font-family: $font-family; line-height: 1.2; } +.root { + display: flex; + flex-direction: column; + + @media(max-width: $toc-width + $max-width) { + margin-right: auto; + margin-left: auto; + width: 100%; + max-width: $max-width; + } +} +.table-of-contents { + order: 0; + @media(max-width: $toc-width + $max-width) { + padding-left: 15px; + padding-right: 15px; + } + + ul { + padding-left: 1.0rem !important; + & > li { + margin-left: 0.3em !important; + } + } + @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 (min-width: $toc-width * 1.5 + $max-width) { + .root { + margin-left: calc((100vw - 750px) / 2); + } + .content { + margin: 0; + } +} + +@media(min-width: $toc-width + $max-width + 25px) { + .root { + flex-direction: row; + } + + .table-of-contents { + width: $toc-width; + order: 2; + position: sticky; + top: 0px; + padding: 1em; + align-self: start; + scrollbar-width: thin; + // flex-grow: 1; + + .table-of-contents-text { + overflow-x: hidden; + overflow-y: auto; + max-height: calc(100vh - 155px); + + + } + } + + .content { + // flex-grow: 1; + } + + #title-small-screen { + display: none; + } + #title-large-screen { + display: block; + } +} + .navbar-brand { @extend %link-dark; font-size: 2rem; diff --git a/layouts/_default/single.html b/layouts/_default/single.html index 140759a..025b4e1 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -1,6 +1,15 @@ {{ define "main" }} -
-

{{ .Title }}

- {{ .Content }} +
+

{{ .Title }}

+
+

{{ .Title }}

+ {{ .Content }} +
+
+
+ Table of Contents + {{ .TableOfContents }} +
+
{{ end }} From fb5996755bd7adb896925918254b438616fd341e Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Sun, 26 Jun 2022 21:29:52 +0300 Subject: [PATCH 2/4] feat(toc): dynamic heading --- .editorconfig | 6 +++- assets/sass/researcher.scss | 47 ++++++++++++++----------- config.toml | 2 ++ layouts/_default/single.html | 3 +- scripts/publish-configs.el | 3 ++ static/js/dynamic-toc.js | 68 ++++++++++++++++++++++++++++++++++++ 6 files changed, 107 insertions(+), 22 deletions(-) create mode 100644 static/js/dynamic-toc.js diff --git a/.editorconfig b/.editorconfig index dfa53db..17feeda 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,4 +4,8 @@ indent_size = 4 [*.scss] indent_style = "space" -indent_size = 4 \ No newline at end of file +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 1ffa21c..b3aa6ab 100644 --- a/assets/sass/researcher.scss +++ b/assets/sass/researcher.scss @@ -49,30 +49,26 @@ $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; - - @media(max-width: $toc-width + $max-width) { - margin-right: auto; - margin-left: auto; - width: 100%; - max-width: $max-width; - } } .table-of-contents { order: 0; - @media(max-width: $toc-width + $max-width) { - padding-left: 15px; - padding-right: 15px; - } - ul { padding-left: 1.0rem !important; & > li { margin-left: 0.3em !important; } } + + a.active { + font-weight: bold; + } + @media(max-width: 578px) { align-self: center; } @@ -92,16 +88,30 @@ $y-medium: 1.0rem; } } +@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); } - .content { + #actual-content { margin: 0; } } -@media(min-width: $toc-width + $max-width + 25px) { +@media(min-width: $toc-left-width) { .root { flex-direction: row; } @@ -114,19 +124,16 @@ $y-medium: 1.0rem; padding: 1em; align-self: start; scrollbar-width: thin; - // flex-grow: 1; .table-of-contents-text { overflow-x: hidden; overflow-y: auto; max-height: calc(100vh - 155px); - - } - } - .content { - // flex-grow: 1; + .hidden { + display: none; + } } #title-small-screen { 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 025b4e1..8038fdd 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -1,7 +1,8 @@ {{ define "main" }} +

{{ .Title }}

-
+

{{ .Title }}

{{ .Content }}
diff --git a/scripts/publish-configs.el b/scripts/publish-configs.el index bf71c9c..353156d 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")) diff --git a/static/js/dynamic-toc.js b/static/js/dynamic-toc.js new file mode 100644 index 0000000..84827c8 --- /dev/null +++ b/static/js/dynamic-toc.js @@ -0,0 +1,68 @@ +const tocId = "TableOfContents"; +const actualContentId = "actual-content"; +let headerObserver; + +function observeHeadings() { + const links = document.querySelectorAll(`#${tocId} a`); + const headings = document.querySelectorAll(`${actualContentId} h1,h2,h3,h4`); + const elemsToHide = []; + const linksById = {}; + + 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) => { + let newActiveLinkId; + for (const entry of entries) { + if (entry.isIntersecting && linksById[`#${entry.target.id}`]) { + newActiveLinkId = `#${entry.target.id}`; + break; + } + } + if (newActiveLinkId) { + for (const link of links) { + link.classList.remove("active"); + } + for (const elem of elemsToHide) { + elem.classList.add("hidden"); + } + linksById[newActiveLinkId].classList.add("active"); + for ( + let elem = linksById[newActiveLinkId]; + (elem = elem.parentElement); + elem.id !== tocId + ) { + elem.classList.remove("hidden"); + } + for (const elem of linksById[newActiveLinkId].parentElement.children) { + elem.classList.remove("hidden"); + } + } + }, + { + threshold: 0.1, + root: document.querySelector(`${actualContentId}`), + } + ); + + for (const heading of headings) { + headerObserver.observe(heading); + } +} + +window.addEventListener("load", (event) => { + if ("IntersectionObserver" in window) { + observeHeadings(); + } +}); + +window.addEventListener("unload", (event) => { + headerObserver.disconnect(); +}); From e0886d64a59fceafc29e0a88b47d2f85263af4b5 Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Sun, 26 Jun 2022 21:45:28 +0300 Subject: [PATCH 3/4] feat(toc): expand/collapse --- assets/sass/researcher.scss | 4 ++ layouts/_default/single.html | 2 + static/js/dynamic-toc.js | 74 +++++++++++++++++++++++++++--------- 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/assets/sass/researcher.scss b/assets/sass/researcher.scss index b3aa6ab..f708d4d 100644 --- a/assets/sass/researcher.scss +++ b/assets/sass/researcher.scss @@ -69,6 +69,10 @@ $toc-left-width: $toc-width + $max-width + 25px; font-weight: bold; } + a:hover { + cursor: pointer; + } + @media(max-width: 578px) { align-self: center; } diff --git a/layouts/_default/single.html b/layouts/_default/single.html index 8038fdd..443deaa 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -11,6 +11,8 @@ Table of Contents {{ .TableOfContents }}
+ +
{{ end }} diff --git a/static/js/dynamic-toc.js b/static/js/dynamic-toc.js index 84827c8..eb31840 100644 --- a/static/js/dynamic-toc.js +++ b/static/js/dynamic-toc.js @@ -1,12 +1,15 @@ const tocId = "TableOfContents"; const actualContentId = "actual-content"; +let showAll = false; +let currentActiveLinkId = null; +let elemsToHide = []; +let linksById = {}; + let headerObserver; function observeHeadings() { const links = document.querySelectorAll(`#${tocId} a`); const headings = document.querySelectorAll(`${actualContentId} h1,h2,h3,h4`); - const elemsToHide = []; - const linksById = {}; for (const link of links) { linksById[link.getAttribute("href")] = link; @@ -19,30 +22,20 @@ function observeHeadings() { headerObserver = new IntersectionObserver( (entries) => { - let newActiveLinkId; for (const entry of entries) { if (entry.isIntersecting && linksById[`#${entry.target.id}`]) { - newActiveLinkId = `#${entry.target.id}`; + currentActiveLinkId = `#${entry.target.id}`; break; } } - if (newActiveLinkId) { + if (currentActiveLinkId) { for (const link of links) { link.classList.remove("active"); } - for (const elem of elemsToHide) { - elem.classList.add("hidden"); - } - linksById[newActiveLinkId].classList.add("active"); - for ( - let elem = linksById[newActiveLinkId]; - (elem = elem.parentElement); - elem.id !== tocId - ) { - elem.classList.remove("hidden"); - } - for (const elem of linksById[newActiveLinkId].parentElement.children) { - elem.classList.remove("hidden"); + linksById[currentActiveLinkId].classList.add("active"); + + if (!showAll) { + hideHeadings(); } } }, @@ -55,11 +48,56 @@ function observeHeadings() { 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"); + } } window.addEventListener("load", (event) => { if ("IntersectionObserver" in window) { observeHeadings(); + observeButtons(); } }); From 92ca9a8341de261dec5ebe597ae3109abc4212ab Mon Sep 17 00:00:00 2001 From: SqrtMinusOne Date: Sun, 26 Jun 2022 21:57:33 +0300 Subject: [PATCH 4/4] fix: images & resize TOC --- scripts/publish-configs.el | 6 +++++- static/js/dynamic-toc.js | 21 ++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/scripts/publish-configs.el b/scripts/publish-configs.el index 353156d..42453e9 100644 --- a/scripts/publish-configs.el +++ b/scripts/publish-configs.el @@ -55,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 index eb31840..2292873 100644 --- a/static/js/dynamic-toc.js +++ b/static/js/dynamic-toc.js @@ -5,7 +5,7 @@ let currentActiveLinkId = null; let elemsToHide = []; let linksById = {}; -let headerObserver; +let headerObserver = null; function observeHeadings() { const links = document.querySelectorAll(`#${tocId} a`); @@ -94,10 +94,25 @@ function showHeadings() { } } +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) { - observeHeadings(); - observeButtons(); + setUpObserver(); + window.addEventListener("resize", setUpObserver); } });