diff --git a/app/assets/javascripts/discourse/app/components/sidebar.js b/app/assets/javascripts/discourse/app/components/sidebar.js index 2235025463c..afb309f54f1 100644 --- a/app/assets/javascripts/discourse/app/components/sidebar.js +++ b/app/assets/javascripts/discourse/app/components/sidebar.js @@ -8,6 +8,8 @@ export default class Sidebar extends GlimmerComponent { if (this.site.mobileView) { document.addEventListener("click", this.collapseSidebar); } + // This appEvent handler is experimental and should not be relied on as an extension point yet. + this.appEvents.on("sidebar:scroll-to-element", this, this.#scrollToElement); } @bind @@ -31,9 +33,68 @@ export default class Sidebar extends GlimmerComponent { } } + #scrollToElement(destinationElement) { + const topPadding = 10; + + const sidebarContainerElement = + document.querySelector(".sidebar-container"); + + const distanceFromTop = + document.getElementsByClassName(destinationElement)[0].offsetTop - + topPadding; + + this.#setMissingHeightForScroll(sidebarContainerElement, distanceFromTop); + + sidebarContainerElement.scrollTop = distanceFromTop; + } + + #setMissingHeightForScroll(sidebarContainerElement, distanceFromTop) { + const allSections = document.getElementsByClassName( + "sidebar-section-wrapper" + ); + + const lastSectionElement = allSections[allSections.length - 1]; + + const lastSectionBottomPadding = parseInt( + lastSectionElement.style.paddingBottom?.replace("px", "") || 0, + 10 + ); + + const headerOffset = parseInt( + document.documentElement.style.getPropertyValue("--header-offset"), + 10 + ); + + let allSectionsHeight = 0; + + for (const section of allSections) { + allSectionsHeight += + section.clientHeight + + parseInt( + window.getComputedStyle(section).marginBottom.replace("px", ""), + 10 + ); + } + + const missingHeight = + sidebarContainerElement.clientHeight - + headerOffset + + lastSectionBottomPadding - + (allSectionsHeight - distanceFromTop); + + lastSectionElement.style.paddingBottom = + missingHeight > 0 ? `${missingHeight}px` : null; + } + willDestroy() { if (this.site.mobileView) { document.removeEventListener("click", this.collapseSidebar); } + + this.appEvents.off( + "sidebar:scroll-to-element", + this, + this.#scrollToElement + ); } } diff --git a/app/assets/stylesheets/common/base/sidebar.scss b/app/assets/stylesheets/common/base/sidebar.scss index 209011472b9..7d7a3fbd6a3 100644 --- a/app/assets/stylesheets/common/base/sidebar.scss +++ b/app/assets/stylesheets/common/base/sidebar.scss @@ -35,6 +35,7 @@ scrollbar-color: transparent var(--scrollbarBg); transition: scrollbar-color 0.25s ease-in-out; transition-delay: 0.5s; + scroll-behavior: smooth; &::-webkit-scrollbar-thumb { background-color: transparent;