Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v2] Add Table of Content #415

Open
luizdepra opened this issue Sep 28, 2020 · 10 comments
Open

[v2] Add Table of Content #415

luizdepra opened this issue Sep 28, 2020 · 10 comments
Labels
enhancement New feature or request v2

Comments

@luizdepra
Copy link
Owner

luizdepra commented Sep 28, 2020

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.

@JianLoong
Copy link
Contributor

I was looking at docsy to see how they approached it. They have it like a side bar.

image

The responsive view for it is pretty nice too. (Happens when the navigation button is selected)

image

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?

@luizdepra
Copy link
Owner Author

luizdepra commented Oct 13, 2020

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. :)

@pinpox
Copy link

pinpox commented Jan 13, 2021

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?

@luizdepra
Copy link
Owner Author

@pinpox, It should go here before {{.Content}}.

@pinpox
Copy link

pinpox commented Jan 23, 2021

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

@luizdepra
Copy link
Owner Author

@pinpox, nop. No need to change your markdowns.

I just did the same thing and tested with exampleSite content.

Screenshot from 2021-01-25 16-15-02

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.

@prishs
Copy link

prishs commented Mar 4, 2021

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?

@ckvv
Copy link
Contributor

ckvv commented Sep 27, 2021

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

截屏2021-09-27 下午3 34 50

截屏2021-09-27 下午3 32 30

  • phone screen

截屏2021-09-27 下午3 32 44

截屏2021-09-27 下午3 34 41

@Oyyko
Copy link

Oyyko commented Jul 31, 2023

Hi. I want to know how is the progress of this feature? Thanks a lot for your development!

@ckvv
Copy link
Contributor

ckvv commented Aug 1, 2023

Hi. I want to know how is the progress of this feature? Thanks a lot for your development!

截屏2023-08-01 13 59 31

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request v2
Projects
None yet
Development

No branches or pull requests

6 participants