DiscoTOC style is broken if there is no reply on mobile

Hello :wave:

DiscoTOC style is connected to the .with-topic-progress class which is added to .topic-navigation if there are more than 1 post in the topic. So without reply the DiscoTOC style will broke.

I think TOC should also be active if there is no reply in a topic because it doesn’t related with the reply counts. On desktop it works fine topics with no reply too.

Here is a topic for testing: Customizing the topic list

I think that would be consider to change this behaviour.

It’s probably not a new thing but I didn’t notice it yet. Now we started it using more actively.

Thanks :slight_smile:

6 Likes

I’ve just run into this here too. :cry: (iPhone/Safari) It’d be great if this could be fixed. Scrolling to the bottom of a long doc to get to the toc is a bit cumbersome.

3 Likes

With my basic coding skills and some ChatGPT talk, I made it work, I believe.

Created a custom component and put this in the CSS tab:

@media (max-width: 767px) {
    #topic-progress-wrapper.sticky-bottom {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 1000;
    }
}

and this in the JS tab

import { apiInitializer } from "discourse/lib/api";

export default apiInitializer((api) => {
  api.onPageChange(() => {
    const observer = new MutationObserver(() => {
      const wrapper = document.getElementById("topic-progress-wrapper");
      const tocButton = wrapper?.querySelector(".d-toc-mini button");

      if (wrapper) {
        if (tocButton) {
          wrapper.classList.add("sticky-bottom");
        } else {
          wrapper.classList.remove("sticky-bottom");
        }
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });
  });
});

This seems to be working just fine even without a reply.
Again, I’m no expert, I’m just a good “asker” and after a little bit of “back and forth” with ChatGPT with the right questions and challenges, it seems to be working.

If anyone who is an expert in JavaScript and CSS wants to improve it, please share.


So, if you want:

  1. The text “Table of Contents” next to the icon
  2. Expanded headings
  3. Sticky button without relying on any replies, the full CSS is this:
/* expands all headings in table of content sidebar */
#d-toc li.d-toc-item > ul {
  max-height: 500em !important;
  overflow: visible !important;
  opacity: 1 !important;
}

@media screen and (max-width: 767px) {
    /* makes the wrapper including the table of contents button, sticky to the bottom */
    #topic-progress-wrapper.sticky-bottom {
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 1000;
    }

    /* add "table ot contents" to the button after the icon */
    .d-toc-mini button::after {
        content: "Table of Contents";
        margin-left: 5px;
        font-size: 14px;
        vertical-align: middle;
    }
}

The JavaScript doesn’t change.

1 Like

Hello :wave:

This is great thanks, but I think we can make it with only CSS too. :thinking: We just have to use the same CSS on .topic-navigation which used on .with-topic-progress. The missing .with-topic-progress class is blocking the style if there is no reply in the topic.

Something like this should work.

Mobile CSS

// Sticky Topic Navigation
.container.posts .topic-navigation:not(.with-topic-progress) {
  position: sticky;
  bottom: calc(env(safe-area-inset-bottom) + var(--composer-height, 0px));
  z-index: z("timeline");
  pointer-events: none; // the wrapper can block mobile controls
  
  > * {
    pointer-events: auto; // this unsets the above rule so the child elements are interactive
  }
  
  // Add TOC Style
  .d-toc-wrapper {
    position: fixed;
    margin-top: 0.25em;
    height: calc(100vh - 50px - var(--header-offset));
    opacity: 0.5;
    right: -100vw;
    top: var(--header-offset);
    width: 75vw;
    max-width: 350px;
    background-color: var(--secondary);
    box-shadow: var(--shadow-dropdown);
    z-index: z("modal", "overlay");
    transition: all 0.2s ease-in-out;

    .d-toc-main {
      width: 100%;
      padding: 0.5em;
      height: 100%;

      #d-toc {
        max-height: calc(100% - 3em);
      }
    }

    &.overlay {
      right: 0;
      width: 75vw;
      opacity: 1;

      .d-toc-main #d-toc li.d-toc-item ul {
        transition: none;
      }
    }

    a.scroll-to-bottom,
    a.d-toc-close {
      display: inline-block;
      padding: 0.5em;
    }

    .d-toc-icons {
      text-align: right;
    }
  }
}

// RTL Support
.rtl {
  .topic-navigation .d-toc-wrapper {
    right: unset;
    left: -100vw;

    &.overlay {
      right: unset;
      left: 0;
    }
  }
}
1 Like