128 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| class Sidebar {
 | |
|   constructor(element) {
 | |
|     this.element = element;
 | |
|     this.activeLink = null;
 | |
| 
 | |
|     this.element.addEventListener('click', (e) => {
 | |
|       // If we click a navigation, close the hamburger menu
 | |
|       if (e.target.tagName == 'A' && this.element.classList.contains('sidebar-toggle')) {
 | |
|         this.element.classList.remove('sidebar-toggle');
 | |
|         let button = hamburgerToggle.firstElementChild;
 | |
|         button.textContent = 'menu';
 | |
| 
 | |
|         // Scroll a little up to actually see the header
 | |
|         // Note: this is generally around ~55px
 | |
|         // A proper solution is getComputedStyle but it can be slow
 | |
|         // Instead let's just rely on this quirk and call it a day
 | |
|         // This has to be done after the browser actually processes
 | |
|         // the section movement
 | |
|         setTimeout(() => window.scrollBy(0, -100), 75);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   createCollapsableSections() {
 | |
|     let toc = this.element.querySelector('ul');
 | |
|     if (!toc) {
 | |
|       return
 | |
|     }
 | |
|     let allReferences = toc.querySelectorAll('a.reference.internal:not([href="#"])');
 | |
| 
 | |
|     for (let ref of allReferences) {
 | |
| 
 | |
|       let next = ref.nextElementSibling;
 | |
| 
 | |
|       if (next && next.tagName === "UL") {
 | |
| 
 | |
|         let icon = document.createElement('span');
 | |
|         icon.className = 'material-icons collapsible-arrow expanded';
 | |
|         icon.innerText = 'expand_more';
 | |
| 
 | |
|         if (next.parentElement.tagName == "LI") {
 | |
|           next.parentElement.classList.add('no-list-style')
 | |
|         }
 | |
| 
 | |
|         icon.addEventListener('click', () => {
 | |
|           if (icon.classList.contains('expanded')) {
 | |
|             this.collapseSection(icon);
 | |
|           } else {
 | |
|             this.expandSection(icon);
 | |
|           }
 | |
|         })
 | |
| 
 | |
|         ref.classList.add('ref-internal-padding')
 | |
|         ref.parentNode.insertBefore(icon, ref);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   resize() {
 | |
|     let rect = this.element.getBoundingClientRect();
 | |
|     this.element.style.height = `calc(100vh - 1em - ${rect.top + document.body.offsetTop}px)`;
 | |
|   }
 | |
| 
 | |
|   collapseSection(icon) {
 | |
|     icon.classList.remove('expanded');
 | |
|     icon.classList.add('collapsed');
 | |
|     let children = icon.nextElementSibling.nextElementSibling;
 | |
|     // <arrow><heading>
 | |
|     // --> <square><children>
 | |
|     setTimeout(() => children.style.display = "none", 75)
 | |
|   }
 | |
| 
 | |
|   expandSection(icon) {
 | |
|     icon.classList.remove('collapse');
 | |
|     icon.classList.add('expanded');
 | |
|     let children = icon.nextElementSibling.nextElementSibling;
 | |
|     setTimeout(() => children.style.display = "block", 75)
 | |
|   }
 | |
| 
 | |
|   setActiveLink(section) {
 | |
|     if (this.activeLink) {
 | |
|       this.activeLink.parentElement.classList.remove('active');
 | |
|     }
 | |
|     if (section) {
 | |
|       this.activeLink = document.querySelector(`#sidebar a[href="#${section.id}"]`);
 | |
|       if (this.activeLink) {
 | |
|         let headingChildren = this.activeLink.parentElement.parentElement;
 | |
|         let heading = headingChildren.previousElementSibling.previousElementSibling;
 | |
| 
 | |
|         if (heading && headingChildren.style.display === 'none') {
 | |
|           this.activeLink = heading;
 | |
|         }
 | |
|         this.activeLink.parentElement.classList.add('active');
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| function getCurrentSection() {
 | |
|   let currentSection;
 | |
|   if (window.scrollY + window.innerHeight > bottomHeightThreshold) {
 | |
|     currentSection = sections[sections.length - 1];
 | |
|   }
 | |
|   else {
 | |
|     if (sections) {
 | |
|       sections.forEach(section => {
 | |
|         let rect = section.getBoundingClientRect();
 | |
|         if (rect.top + document.body.offsetTop < 1) {
 | |
|           currentSection = section;
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|   }
 | |
|   return currentSection;
 | |
| }
 | |
| 
 | |
| document.addEventListener('DOMContentLoaded', () => {
 | |
|   sidebar = new Sidebar(document.getElementById('sidebar'));
 | |
|   sidebar.resize();
 | |
|   sidebar.createCollapsableSections();
 | |
| 
 | |
|   window.addEventListener('scroll', () => {
 | |
|     sidebar.setActiveLink(getCurrentSection());
 | |
|     sidebar.resize();
 | |
|   });
 | |
| });
 |