hugo-coder
hugo-coder copied to clipboard
[v2] Add Table of Content
Add TOC support.
I need to decide with style to use first. (I didn't like the aside style. I'll probably go with the on top style.) See discussion in #329.
I was looking at docsy to see how they approached it. They have it like a side bar.
The responsive view for it is pretty nice too. (Happens when the navigation button is selected)
Their approach seems to be like "side bar" normally but changes to be "on top" when the screen becomes smaller. Seems to be like one of the approaches that can be used.
That being said also, if it is a side bar, when users navigate down on the main contents, they would be navigating away from the side bar. (Depending on the amount of contents in the sidebar table of contents). So should the it be in a fixed position at the side? Meaning when the user navigates the main contents, the sidebar position stays the same?
With regards to the top style, it seems that it might be too many contents there causing it to occupy too much space.
What do you think of their approach?
I think a side like docsy have doesn't fit well with coder's style. LoveIt, that someone mentioned in #329, looks like a better option. It also stays on top in smaller screens.
But I still have mixed feelings about this issue. I really don't know yet which of these approaches is the better one.
OFFTOPIC: I just discovered that LoveIt is based on other themes that are based on coder, and this is very nice. :)
Is it possible to render any table of contents at all (even if not styled yet) with this theme?
I added {{ .TableOfContents }}
to ./themes/hugo-coder/layouts/posts
but the variable {{ .TableOfContents }}
seems to be empty. It does not error when rendering, so the variable seems to exist, even though it is not set to anything.
Any help on how to get a simple TOC?
@pinpox, It should go here before
{{.Content}}
.
@luizdepra This is my themes/hugo-coder/layouts/posts/single.html
file:
{{ define "title" }}
{{ .Title }} · {{ .Site.Title }}
{{ end }}
{{ define "content" }}
<section class="container post">
<article>
<header>
<div class="post-title">
<h1 class="title">{{ .Title }}</h1>
</div>
<div class="post-meta">
<div class="date">
<span class="posted-on">
<i class="fa fa-calendar" aria-hidden="true"></i>
<time datetime='{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}'>
{{ .Date.Format (.Site.Params.dateFormat | default "January 2, 2006" ) }}
</time>
</span>
<span class="reading-time">
<i class="fa fa-clock-o" aria-hidden="true"></i>
{{ i18n "reading_time" .ReadingTime }}
</span>
</div>
{{ with .Page.Params.Authors }}{{ partial "taxonomy/authors.html" . }}{{ end }}
{{ with .Page.Params.Categories }}{{ partial "taxonomy/categories.html" . }}{{ end }}
{{ with .Page.Params.Tags }}{{ partial "taxonomy/tags.html" . }}{{ end }}
</div>
</header>
<div>
{{ if .Params.featured_image }}
<img src='{{ .Params.featured_image }}' alt="Featured image"/>
{{ end }}
{{ .TableOfContents }}
{{ .Content }}
</div>
<footer>
{{ partial "posts/series.html" . }}
{{ partial "posts/disqus.html" . }}
{{ partial "posts/commento.html" . }}
{{ partial "posts/utterances.html" . }}
</footer>
</article>
{{ partial "posts/math.html" . }}
</section>
{{ end }}
But I dont' see any a ToC in my posts when running hugo serve
. Do I have to add it in the posts's markdown too?
@pinpox, nop. No need to change your markdowns.
I just did the same thing and tested with exampleSite
content.
Are you certain that you are testing against a post with correct headings? By default, the TOC you not show if no displayable headings are present. Also, level 1 headings will not show unless configured to do so. See TOC configurations here.
If you are using VScode you can create this type of TOC using "markdown all in one" plugin. And on top TOC goes out of sight when you scroll down. So I was wondering if something like this TOC and go-to-up arrow would be good choice?
I added a table of contents to my blog。 if you think this style is ok, I would be happy to create a pull requset
https://chenkai.life/web/handling-fonts-in-the-web
-
Widescreen
-
phone screen
![截屏2021-09-27 下午3 32 44](https://user-images.githubusercontent.com/30174970/134864928-369b234a-4f28-46a9-8b58-8c30138743ed.png)
![截屏2021-09-27 下午3 34 41](https://user-images.githubusercontent.com/30174970/134864942-a77f8ca4-743b-4cab-b445-7c0dee5796a7.png)
Hi. I want to know how is the progress of this feature? Thanks a lot for your development!
Hi. I want to know how is the progress of this feature? Thanks a lot for your development!
you can try import this script
function debounce(func, wait, options = {
immediate: false,
middle: true,
thisArg: null,
}) {
let timer;
let restDate = new Date();
const immediate = options.immediate !== false;
const middle = options.middle !== false;
const thisArg = options.thisArg || null;
return function (...args) {
timer && clearTimeout(timer);
let isFirst = !timer;
timer = setTimeout(() => {
func.apply(thisArg, args);
restDate = new Date();
}, wait);
if ((new Date() - restDate > wait && middle) || (isFirst && immediate)) {
clearTimeout(timer);
func.apply(thisArg, args);
restDate = new Date();
}
}
}
function setActive(anchors) {
const ele = anchors.find((ele, index, arr) => {
return ele.getBoundingClientRect().top >= 0 || index >= arr.length - 1;
});
if (ele) {
const tableOfContents = document.querySelector('#table-of-contents');
const toActive = tableOfContents.querySelector(`a[href="#${ele.id}"]`);
if (!toActive) return;
const activeA = tableOfContents.querySelector(`.active`);
if (activeA) activeA.classList.remove('active');
toActive.classList.add('active');
window.history.pushState(null, null, `#${ele.id}`);
tableOfContents.scrollTo({
left: 0,
top: toActive.offsetTop - tableOfContents.getBoundingClientRect().height / 2,
behavior: 'smooth',
});
}
}
function initContents(icon = '<svg t="1690868184633" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3381" width="32" height="32"><path d="M128 192l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3382"></path><path d="M128 448l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3383"></path><path d="M128 704l768 0 0 128-768 0 0-128Z" fill="#666666" p-id="3384"></path></svg>') {
if(document.querySelector('#table-of-contents-wapper')) return;
const contents = document.createElement('details');
contents.id = 'table-of-contents-wapper';
contents.innerHTML = `<summary>
${icon}
</summary>`
const styleElement = document.createElement('style')
styleElement.innerHTML = `#table-of-contents-wapper {
user-select: none;
position: fixed;
right: 1em;
top: 4em;
border-radius: 8px;
z-index: 999
}
@media only screen and (min-width:768px) {
#table-of-contents-wapper[open] summary {
position: relative;
left: 5.8em
}
}
@media only screen and (max-width:768px) {
#table-of-contents-wapper {
top: auto;
right: auto;
bottom: 1.8rem;
left: 1.8rem
}
}
#table-of-contents-wapper summary {
display: inline-block;
font-size: 1.5em;
border-radius: 4px;
cursor: pointer;
padding: .2em
}
#table-of-contents-wapper #table-of-contents {
line-height: 1.3;
width: 19rem;
font-size: .8rem;
padding: .8em;
border: 1px solid;
border-radius: 6px;
overflow-y: scroll;
max-height: calc(100vh - 20rem);
color: currentColor
}
#table-of-contents-wapper #table-of-contents a {
width: 100%;
display: inline-block;
color: currentColor;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis
}
#table-of-contents-wapper #table-of-contents .active {
border-left: 2px solid #42a5f5;
color: #42a5f5
}
.content h1,
.content h2,
.content h3 {
padding-top: 2em !important;
margin-top: -2em !important
}`;
const anchors = [...document.querySelector('.content').querySelectorAll('h1[id],h2[id],h3[id],h4[id]')];
if (!anchors.length) return contents.remove();
const tableOfContents = document.createElement('div');
tableOfContents.id = 'table-of-contents';
anchors.forEach(ele => {
const a = document.createElement('a');
if (!ele.innerText) return;
a.innerText = ele.innerText;
a.href = `#${ele.id}`;
a.style.paddingLeft = `${ele.tagName.charAt(1)}em`;
tableOfContents.appendChild(a);
});
contents.appendChild(tableOfContents);
contents.open = window.innerWidth >= 768;
document.head.appendChild(styleElement);
document.body.append(contents);
setActive(anchors);
const debounceSetActive = debounce(setActive, 200)
window.addEventListener('scroll', () => {
debounceSetActive(anchors);
});
}
and run the initContents
function