diff --git a/assets/sprite.svg b/assets/sprite.svg
index ea133f1..2542006 100644
--- a/assets/sprite.svg
+++ b/assets/sprite.svg
@@ -1,5 +1,5 @@
-
-
+
+
@@ -14,5 +14,12 @@
-
-
+
+
+
+
+
+
+
+
+
diff --git a/common/common.scss b/common/common.scss
index 55e8502..3b153cf 100644
--- a/common/common.scss
+++ b/common/common.scss
@@ -1,26 +1,41 @@
$padding-basis: 0.75em;
+.d-toc-available {
+ // the toc increases the timeline container's width
+ // so as long as the toc is available in a topic
+ // we want this width to remain for the timeline
+ // so if the toc is toggled off, the position doesn't shift
+
+ @media screen and (max-width: 1109px) {
+ .d-toc-wrapper {
+ width: 10em;
+ }
+ }
+
+ @media screen and (min-width: 1110px) {
+ .container.posts {
+ grid-template-columns: auto 14.6em;
+ }
+ }
+}
+
.d-toc-main {
display: none;
- width: 225px;
- @media screen and (max-width: 1045px) {
- .desktop-view & {
- width: 150px;
- }
- }
border-left: 1px solid var(--primary-low);
box-sizing: border-box;
a {
display: block;
padding: 0.15em 0;
- color: var(--primary-medium);
+ color: var(--primary-700);
&.scroll-to-bottom {
+ color: var(--tertiary);
padding-left: $padding-basis;
+ margin-top: 0.5em;
+ font-size: var(--font-down-1);
}
}
#d-toc {
- max-height: calc(100vh - 4.5em - var(--header-offset));
- padding-bottom: 0.5em;
+ max-height: calc(100dvh - 7em - var(--header-offset));
overflow: auto;
ul {
list-style-type: none;
@@ -47,7 +62,7 @@ $padding-basis: 0.75em;
}
}
> a:hover {
- color: var(--primary-high);
+ color: var(--primary);
}
&.direct-active > a {
position: relative;
@@ -124,6 +139,53 @@ a.d-toc-close {
display: none;
}
+.d-toc-timeline-toggle {
+ display: none;
+ position: fixed;
+ bottom: 0;
+ @include ellipsis;
+ .d-icon-timeline {
+ margin-right: 0.25em;
+ }
+
+ @media screen and (min-height: 1200px) {
+ // on tall screens we don't want this button
+ // to be incredibly distant
+ position: absolute;
+ .d-toc-timeline-visible & {
+ bottom: -4em;
+ }
+ bottom: -6.5em;
+ }
+}
+
+.topic-navigation.with-timeline:has(.timeline-docked) {
+ // hide toggle when timeline is docked
+ // (firefox doesn't support :has yet)
+ .d-toc-timeline-toggle {
+ display: none;
+ }
+}
+
+.d-toc-timeline-toggleable {
+ .d-toc-timeline-toggle {
+ display: block;
+ z-index: z("timeline");
+ @media screen and (max-height: 480px) {
+ // avoid overlapping timeline
+ display: none;
+ }
+ }
+ .with-topic-progress {
+ .d-toc-main {
+ display: block;
+ }
+ .d-toc-timeline-toggle {
+ display: none;
+ }
+ }
+}
+
.d-toc-timeline-visible {
.d-toc-main,
.d-toc-mini {
@@ -134,7 +196,7 @@ a.d-toc-close {
.d-toc-wrapper {
position: fixed;
margin-top: 0.25em;
- height: calc(100vh - 50px - var(--header-offset));
+ height: calc(100dvh - 50px - var(--header-offset));
opacity: 0.5;
right: -100vw;
top: var(--header-offset);
@@ -149,18 +211,25 @@ a.d-toc-close {
padding: 0.5em;
height: 100%;
#d-toc {
- max-height: calc(100% - 3em);
+ max-height: calc(100% - 2.25em);
}
}
&.overlay {
right: 0;
width: 75vw;
opacity: 1;
- .d-toc-main #d-toc li.d-toc-item ul {
- transition: none;
+ .d-toc-main {
+ display: block;
+ #d-toc li.d-toc-item ul {
+ transition: none;
+ }
}
}
+ a.scroll-to-bottom {
+ margin-top: 0.33em;
+ }
+
a.scroll-to-bottom,
a.d-toc-close {
display: inline-block;
@@ -168,14 +237,17 @@ a.d-toc-close {
}
.d-toc-icons {
- text-align: right;
+ position: absolute;
+ background: var(--secondary);
+ right: 1.5em;
+ top: 0.25em;
+ z-index: z("timeline");
}
}
}
// core overrides when timeline is active
- .timeline-container,
- #topic-progress {
+ .timeline-container {
display: none;
}
.container.posts .topic-navigation.with-topic-progress {
@@ -183,6 +255,26 @@ a.d-toc-close {
}
}
+#topic-progress-wrapper {
+ align-items: stretch;
+ .d-toc-mini {
+ display: none;
+ .d-toc-timeline-visible & {
+ display: block;
+ }
+ height: 100%;
+ .btn {
+ height: 100%;
+ }
+ }
+
+ .staff & {
+ .topic-admin-menu-button-container {
+ margin-left: 0.5em;
+ }
+ }
+}
+
// core sets first child's top margin to 0
// ensure it's also 0 when TOC markup is first
.cooked > div[data-theme-toc]:first-child + * {
@@ -232,11 +324,5 @@ a.d-toc-close {
.below-docs-topic-outlet.d-toc-wrapper {
position: sticky;
top: calc(var(--header-offset, 60px) + 1em);
- max-height: calc(100vh - 2em - var(--header-offset, 60px));
- .mobile-view & {
- display: none;
- }
- .d-toc-main {
- display: block;
- }
+ max-height: calc(100dvh - 2em - var(--header-offset, 60px));
}
diff --git a/javascripts/discourse/connectors/after-topic-progress/d-toc-mini.hbs b/javascripts/discourse/connectors/after-topic-progress/d-toc-mini.hbs
deleted file mode 100644
index b26df5d..0000000
--- a/javascripts/discourse/connectors/after-topic-progress/d-toc-mini.hbs
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/javascripts/discourse/connectors/before-topic-progress/d-toc-mini.hbs b/javascripts/discourse/connectors/before-topic-progress/d-toc-mini.hbs
new file mode 100644
index 0000000..93dddd4
--- /dev/null
+++ b/javascripts/discourse/connectors/before-topic-progress/d-toc-mini.hbs
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/javascripts/discourse/connectors/after-topic-progress/d-toc-mini.js b/javascripts/discourse/connectors/before-topic-progress/d-toc-mini.js
similarity index 72%
rename from javascripts/discourse/connectors/after-topic-progress/d-toc-mini.js
rename to javascripts/discourse/connectors/before-topic-progress/d-toc-mini.js
index 41476f0..3f89217 100644
--- a/javascripts/discourse/connectors/after-topic-progress/d-toc-mini.js
+++ b/javascripts/discourse/connectors/before-topic-progress/d-toc-mini.js
@@ -6,4 +6,9 @@ export default class DTocMini extends Component {
showTOCOverlay() {
document.querySelector(".d-toc-wrapper").classList.toggle("overlay");
}
+
+ @action
+ resetBodyClass() {
+ document.body.classList.add("d-toc-timeline-visible");
+ }
}
diff --git a/javascripts/discourse/initializers/disco-toc-main.js b/javascripts/discourse/initializers/disco-toc-main.js
index f88561e..8a984eb 100644
--- a/javascripts/discourse/initializers/disco-toc-main.js
+++ b/javascripts/discourse/initializers/disco-toc-main.js
@@ -6,6 +6,8 @@ import { iconHTML } from "discourse-common/lib/icon-library";
import domUtils from "discourse-common/utils/dom-utils";
import I18n from "I18n";
+let TOChidden = localStorage.getItem("discourse_toc-hidden") === "true";
+
export default {
name: "disco-toc-main",
@@ -80,13 +82,29 @@ export default {
);
api.onAppEvent("topic:current-post-changed", (args) => {
- if (!document.querySelector(".d-toc-cooked")) {
- return;
+ // manages the timeline area width via CSS
+ if (document.querySelector(".d-toc-cooked")) {
+ document.body.classList.add("d-toc-available");
}
+
+ // manages timeline visibility
if (args.post.post_number === 1) {
- document.body.classList.add("d-toc-timeline-visible");
+ if (!TOChidden) {
+ handleButtonAndBody("show");
+ } else {
+ handleButtonAndBody("hide");
+ }
+
+ // don't show the toggle if there's only 1 post
+ if (args.post.topic.posts_count !== 1) {
+ document.body.classList.add("d-toc-timeline-toggleable");
+ }
} else {
- document.body.classList.remove("d-toc-timeline-visible");
+ handleButtonAndBody("hide");
+ }
+
+ if (args.post.topic.posts_count === 1) {
+ document.body.classList.remove("d-toc-timeline-toggleable");
}
});
@@ -103,7 +121,7 @@ export default {
});
api.cleanupStream(() => {
- document.body.classList.remove("d-toc-timeline-visible");
+ document.body.classList.remove("d-toc-available");
document.removeEventListener("click", this.clickTOC, false);
});
});
@@ -150,30 +168,67 @@ export default {
}
},
- insertTOC(headings) {
+ createMainDiv() {
const dToc = document.createElement("div");
dToc.classList.add("d-toc-main");
- dToc.innerHTML = ``;
+ dToc.innerHTML = ``;
+ return dToc;
+ },
+
+ createScrollLink() {
+ const scrollLink = document.createElement("a");
+ scrollLink.href = "#";
+ scrollLink.className = "scroll-to-bottom";
+ scrollLink.title = I18n.t(themePrefix("post_bottom_tooltip"));
+ scrollLink.innerHTML = `${iconHTML("downward")} ${I18n.t(
+ themePrefix("jump_bottom")
+ )}`;
+ return scrollLink;
+ },
+
+ createToggleButton() {
+ const toggleButton = document.createElement("button");
+ toggleButton.className =
+ "d-toc-timeline-toggle btn btn-default btn-icon-text";
+ toggleButton.innerHTML =
+ iconHTML("timeline") + I18n.t(themePrefix("topic_timeline"));
+
+ return toggleButton;
+ },
+
+ insertTOC(headings) {
+ const dToc = this.createMainDiv();
+ const scrollLink = this.createScrollLink();
+ this.toggleButton = this.createToggleButton();
const existing = document.querySelector(".d-toc-wrapper .d-toc-main");
+ const wrapper = document.querySelector(".d-toc-wrapper");
+
if (existing) {
- document.querySelector(".d-toc-wrapper").replaceChild(dToc, existing);
+ wrapper.replaceChild(dToc, existing);
} else {
- document.querySelector(".d-toc-wrapper").appendChild(dToc);
+ wrapper.appendChild(dToc);
+ wrapper.appendChild(this.toggleButton);
}
const result = this.buildTOC(Array.from(headings));
- document.querySelector(".d-toc-main").appendChild(result);
+ dToc.appendChild(result);
+ dToc.appendChild(scrollLink);
document.addEventListener("click", this.clickTOC, false);
},
clickTOC(e) {
const classNames = ["d-toc-timeline-visible", "archetype-docs-topic"];
+ // toggle timeline visibility
+ if (e.target.closest(".d-toc-timeline-toggle")) {
+ handleButtonAndBody("toggle");
+ e.preventDefault();
+ return false;
+ }
+
if (
!classNames.some((className) =>
document.body.classList.contains(className)
@@ -212,7 +267,7 @@ export default {
top: rect.bottom + window.scrollY - headerOffset() - 10,
behavior: "smooth",
});
-
+ document.querySelector(".d-toc-wrapper").classList.remove("overlay");
e.preventDefault();
return false;
}
@@ -317,3 +372,42 @@ function parentsUntil(el, selector, filter) {
}
return result;
}
+
+function handleButtonAndBody(action) {
+ const body = document.body;
+ const button = document.querySelector("button.d-toc-timeline-toggle");
+
+ switch (action) {
+ case "toggle":
+ body.classList.toggle("d-toc-timeline-visible");
+ TOChidden = !TOChidden;
+ localStorage.setItem("discourse_toc-hidden", TOChidden.toString());
+
+ break;
+
+ case "hide":
+ body.classList.remove(
+ "d-toc-timeline-visible",
+ "d-toc-timeline-toggleable"
+ );
+ break;
+
+ case "show":
+ body.classList.add("d-toc-timeline-visible", "d-toc-timeline-toggleable");
+ break;
+ }
+
+ if (button) {
+ const translationKey = body.classList.contains("d-toc-timeline-visible")
+ ? "topic_timeline"
+ : "table_of_contents";
+
+ const icon = body.classList.contains("d-toc-timeline-visible")
+ ? "timeline"
+ : "stream";
+
+ button.innerHTML = `${iconHTML(icon)}${I18n.t(
+ themePrefix(translationKey)
+ )} `;
+ }
+}
diff --git a/locales/en.yml b/locales/en.yml
index 7b587ec..594af2a 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -1,5 +1,7 @@
en:
- table_of_contents: table of contents
+ table_of_contents: Table of contents
+ topic_timeline: Topic timeline
+ jump_bottom: Jump to replies
insert_table_of_contents: Insert table of contents
post_bottom_tooltip: Navigate to post controls
theme_metadata:
diff --git a/test/acceptance/toc-test.js b/test/acceptance/toc-test.js
index bf34a6b..237fafb 100644
--- a/test/acceptance/toc-test.js
+++ b/test/acceptance/toc-test.js
@@ -1,4 +1,4 @@
-import { visit } from "@ember/test-helpers";
+import { click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import topicFixtures from "discourse/tests/fixtures/topic";
import {
@@ -66,6 +66,14 @@ acceptance("DiscoTOC - main", function (needs) {
"heading gets an ID even when it has no Latin characters"
);
});
+
+ test("TOC can be toggled to reveal timeline", async function (assert) {
+ await visit("/t/internationalization-localization/280");
+ assert.ok(exists(".d-toc-timeline-toggle"), "TOC toggle exists");
+
+ await click(".d-toc-timeline-toggle");
+ assert.ok(exists(".topic-timeline"), "The timeline is shown on toggle");
+ });
});
acceptance("DiscoTOC - off", function (needs) {