Merge pull request #2 from SqrtMinusOne/toc

Toc
This commit is contained in:
Pavel Korytov 2022-06-26 22:03:58 +03:00 committed by GitHub
commit 9f0766a910
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 252 additions and 5 deletions

View file

@ -1,3 +1,11 @@
[*.html]
indent_style = "space"
indent_size = 4
indent_size = 4
[*.scss]
indent_style = "space"
indent_size = 4
[*.js]
indent_style = "space"
indent_size = 2

View file

@ -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;

View file

@ -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

View file

@ -1,6 +1,18 @@
{{ define "main" }}
<div class="container">
<h1>{{ .Title }}</h1>
{{ .Content }}
<script defer language="javascript" type="text/javascript" src="{{ "/js/dynamic-toc.js" | urlize | relURL }}"></script>
<div class="root">
<h1 id="title-small-screen">{{ .Title }}</h1>
<div class="container" id="actual-content">
<h1 id="title-large-screen">{{ .Title }}</h1>
{{ .Content }}
</div>
<div class="table-of-contents">
<div class="table-of-contents-text">
<b><a href="#">Table of Contents</a></b>
{{ .TableOfContents }}
</div>
<a id="unhide-all-button" class="hidden">&lt;Expand&gt;</a>
<a id="hide-all-button" class="hidden">&lt;Collapse&gt;</a>
</div>
</div>
{{ end }}

View file

@ -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

121
static/js/dynamic-toc.js Normal file
View file

@ -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();
});