Rust library to generate static websites

refactor: bundle scripts

+133 -105
+100
website/assets/docs-sidebar.ts
··· 1 + document.addEventListener("DOMContentLoaded", function () { 2 + const leftSidebarToggleElement = document.getElementById( 3 + "left-sidebar-toggle" 4 + ); 5 + const rightSidebarToggleElement = document.getElementById( 6 + "right-sidebar-toggle" 7 + ); 8 + const leftSidebarElement = document.getElementById("mobile-left-sidebar"); 9 + const rightSidebarElement = document.getElementById("mobile-right-sidebar"); 10 + 11 + // Early return if required elements are not found 12 + if ( 13 + !leftSidebarToggleElement || 14 + !rightSidebarToggleElement || 15 + !leftSidebarElement || 16 + !rightSidebarElement 17 + ) { 18 + throw new Error("Sidebar elements not found in the DOM"); 19 + } 20 + 21 + // Now we can safely assign to non-nullable variables 22 + const leftSidebarToggle = leftSidebarToggleElement; 23 + const rightSidebarToggle = rightSidebarToggleElement; 24 + const leftSidebar = leftSidebarElement; 25 + const rightSidebar = rightSidebarElement; 26 + 27 + let leftOpen = false; 28 + let rightOpen = false; 29 + 30 + function toggleLeftSidebar() { 31 + leftOpen = !leftOpen; 32 + 33 + leftSidebar.classList.toggle("-translate-x-full", !leftOpen); 34 + leftSidebar.classList.toggle("translate-x-0", leftOpen); 35 + leftSidebar.classList.toggle("opacity-0", !leftOpen); 36 + leftSidebar.classList.toggle("opacity-100", leftOpen); 37 + leftSidebar.classList.toggle("pointer-events-none", !leftOpen); 38 + 39 + if (leftOpen) { 40 + document.body.style.overflow = "hidden"; 41 + } else if (!rightOpen) { 42 + document.body.style.overflow = ""; 43 + } 44 + } 45 + 46 + function toggleRightSidebar() { 47 + rightOpen = !rightOpen; 48 + 49 + rightSidebar.classList.toggle("translate-x-full", !rightOpen); 50 + rightSidebar.classList.toggle("translate-x-0", rightOpen); 51 + rightSidebar.classList.toggle("opacity-0", !rightOpen); 52 + rightSidebar.classList.toggle("opacity-100", rightOpen); 53 + rightSidebar.classList.toggle("pointer-events-none", !rightOpen); 54 + 55 + if (rightOpen) { 56 + document.body.style.overflow = "hidden"; 57 + } else if (!leftOpen) { 58 + document.body.style.overflow = ""; 59 + } 60 + } 61 + 62 + // Close sidebars when clicking outside 63 + function closeSidebars(event: MouseEvent) { 64 + const target = event.target; 65 + if ( 66 + leftOpen && 67 + target && 68 + !leftSidebar.contains(target as Node) && 69 + !leftSidebarToggle.contains(target as Node) 70 + ) { 71 + toggleLeftSidebar(); 72 + } 73 + if ( 74 + rightOpen && 75 + target && 76 + !rightSidebar.contains(target as Node) && 77 + !rightSidebarToggle.contains(target as Node) 78 + ) { 79 + toggleRightSidebar(); 80 + } 81 + } 82 + 83 + leftSidebarToggle.addEventListener("click", toggleLeftSidebar); 84 + rightSidebarToggle.addEventListener("click", toggleRightSidebar); 85 + document.addEventListener("click", closeSidebars); 86 + 87 + // Close right sidebar when clicking on table of contents links 88 + rightSidebar.addEventListener("click", function (event) { 89 + const target = event.target as HTMLElement; 90 + if ( 91 + target && 92 + target.tagName === "A" && 93 + target.getAttribute("href")?.startsWith("#") 94 + ) { 95 + if (rightOpen) { 96 + toggleRightSidebar(); 97 + } 98 + } 99 + }); 100 + });
+28
website/assets/mobile-menu.ts
··· 1 + document.addEventListener("DOMContentLoaded", function () { 2 + const menuButton = document.getElementById("mobile-menu-button"); 3 + const panel = document.getElementById("mobile-menu-panel"); 4 + const hamburgerIcon = document.getElementById("hamburger-icon"); 5 + const closeIcon = document.getElementById("close-icon"); 6 + let isOpen = false; 7 + 8 + function toggleMenu() { 9 + if (!panel) return; 10 + 11 + isOpen = !isOpen; 12 + 13 + panel.classList.toggle("-translate-x-4", !isOpen); 14 + panel.classList.toggle("opacity-0", !isOpen); 15 + panel.classList.toggle("pointer-events-none", !isOpen); 16 + 17 + if (hamburgerIcon) { 18 + hamburgerIcon.classList.toggle("hidden", isOpen); 19 + } 20 + if (closeIcon) { 21 + closeIcon.classList.toggle("hidden", !isOpen); 22 + } 23 + 24 + document.body.style.overflow = isOpen ? "hidden" : ""; 25 + } 26 + 27 + menuButton?.addEventListener("click", toggleMenu); 28 + });
+2 -77
website/src/layout.rs
··· 75 75 headings: &[MarkdownHeading], 76 76 seo: Option<SeoMeta>, 77 77 ) -> impl Into<RenderResult> { 78 + ctx.assets.include_script("assets/docs-sidebar.ts"); 79 + 78 80 layout( 79 81 html! { 80 82 // Second header for docs navigation (mobile only) ··· 115 117 aside."py-8".hidden."sm:block" { 116 118 (right_sidebar(headings)) 117 119 } 118 - } 119 - 120 - script { 121 - (PreEscaped(r#" 122 - document.addEventListener('DOMContentLoaded', function() { 123 - const leftSidebarToggle = document.getElementById('left-sidebar-toggle'); 124 - const rightSidebarToggle = document.getElementById('right-sidebar-toggle'); 125 - const leftSidebar = document.getElementById('mobile-left-sidebar'); 126 - const rightSidebar = document.getElementById('mobile-right-sidebar'); 127 - 128 - let leftOpen = false; 129 - let rightOpen = false; 130 - 131 - function toggleLeftSidebar() { 132 - leftOpen = !leftOpen; 133 - 134 - leftSidebar.classList.toggle('-translate-x-full', !leftOpen); 135 - leftSidebar.classList.toggle('translate-x-0', leftOpen); 136 - leftSidebar.classList.toggle('opacity-0', !leftOpen); 137 - leftSidebar.classList.toggle('opacity-100', leftOpen); 138 - leftSidebar.classList.toggle('pointer-events-none', !leftOpen); 139 - 140 - if (leftOpen) { 141 - document.body.style.overflow = 'hidden'; 142 - } else if (!rightOpen) { 143 - document.body.style.overflow = ''; 144 - } 145 - } 146 - 147 - function toggleRightSidebar() { 148 - rightOpen = !rightOpen; 149 - 150 - rightSidebar.classList.toggle('translate-x-full', !rightOpen); 151 - rightSidebar.classList.toggle('translate-x-0', rightOpen); 152 - rightSidebar.classList.toggle('opacity-0', !rightOpen); 153 - rightSidebar.classList.toggle('opacity-100', rightOpen); 154 - rightSidebar.classList.toggle('pointer-events-none', !rightOpen); 155 - 156 - if (rightOpen) { 157 - document.body.style.overflow = 'hidden'; 158 - } else if (!leftOpen) { 159 - document.body.style.overflow = ''; 160 - } 161 - } 162 - 163 - // Close sidebars when clicking outside 164 - function closeSidebars(event) { 165 - if (leftOpen && !leftSidebar.contains(event.target) && !leftSidebarToggle.contains(event.target)) { 166 - toggleLeftSidebar(); 167 - } 168 - if (rightOpen && !rightSidebar.contains(event.target) && !rightSidebarToggle.contains(event.target)) { 169 - toggleRightSidebar(); 170 - } 171 - } 172 - 173 - leftSidebarToggle.addEventListener('click', toggleLeftSidebar); 174 - rightSidebarToggle.addEventListener('click', toggleRightSidebar); 175 - document.addEventListener('click', closeSidebars); 176 - 177 - // Close right sidebar when clicking on table of contents links 178 - rightSidebar.addEventListener('click', function(event) { 179 - if (event.target.tagName === 'A' && event.target.getAttribute('href').startsWith('#')) { 180 - if (rightOpen) { 181 - toggleRightSidebar(); 182 - } 183 - } 184 - }); 185 - 186 - // Close sidebars on escape key 187 - document.addEventListener('keydown', function(event) { 188 - if (event.key === 'Escape') { 189 - if (leftOpen) toggleLeftSidebar(); 190 - if (rightOpen) toggleRightSidebar(); 191 - } 192 - }); 193 - }); 194 - "#)) 195 120 } 196 121 }, 197 122 true,
+3 -28
website/src/layout/header.rs
··· 3 3 use maud::PreEscaped; 4 4 use maudit::route::PageContext; 5 5 6 - pub fn header(_: &mut PageContext, bottom_border: bool) -> Markup { 6 + pub fn header(ctx: &mut PageContext, bottom_border: bool) -> Markup { 7 + ctx.assets.include_script("assets/mobile-menu.ts"); 8 + 7 9 let border = if bottom_border { "border-b" } else { "" }; 8 10 let nav_links = vec![ 9 11 ("/docs/", "Documentation"), ··· 76 78 } 77 79 } 78 80 } 79 - } 80 - 81 - script { 82 - (PreEscaped(r#" 83 - document.addEventListener('DOMContentLoaded', function() { 84 - const menuButton = document.getElementById('mobile-menu-button'); 85 - const panel = document.getElementById('mobile-menu-panel'); 86 - const hamburgerIcon = document.getElementById('hamburger-icon'); 87 - const closeIcon = document.getElementById('close-icon'); 88 - let isOpen = false; 89 - 90 - function toggleMenu() { 91 - isOpen = !isOpen; 92 - 93 - panel.classList.toggle('-translate-x-4', !isOpen); 94 - panel.classList.toggle('opacity-0', !isOpen); 95 - panel.classList.toggle('pointer-events-none', !isOpen); 96 - 97 - hamburgerIcon.classList.toggle('hidden', isOpen); 98 - closeIcon.classList.toggle('hidden', !isOpen); 99 - 100 - document.body.style.overflow = isOpen ? 'hidden' : ''; 101 - } 102 - 103 - menuButton.addEventListener('click', toggleMenu); 104 - }); 105 - "#)) 106 81 } 107 82 } 108 83 }