@@ -9,6 +9,7 @@ import Search from "./Search.svelte";
99import DisplaySettings from " ./widget/DisplaySettings.svelte" ;
1010import DropdownMenu from " ./widget/DropdownMenu.astro" ;
1111import TranslateButton from " ./widget/TranslateButton.svelte" ;
12+ import NavMenuPanel from " ./widget/NavMenuPanel.astro" ;
1213
1314const className = Astro .props .class ;
1415
@@ -51,8 +52,12 @@ let links: NavBarLink[] = navBarConfig.links.map(
5152 <div class =" hidden sm:block" >
5253 <LightDarkSwitch client:only =" svelte" ></LightDarkSwitch >
5354 </div >
55+ <button aria-label =" Menu" class =" btn-plain scale-animation rounded-lg w-11 h-11 active:scale-90 md:hidden" id =" nav-menu-switch" >
56+ <Icon name =" material-symbols:menu-rounded" class =" text-[1.25rem]" ></Icon >
57+ </button >
5458 </div >
5559 <DisplaySettings client:only =" svelte" ></DisplaySettings >
60+ <NavMenuPanel links ={ links } ></NavMenuPanel >
5661 </div >
5762</div >
5863
@@ -163,8 +168,7 @@ function closeAllDropdowns() {
163168
164169function toggleDropdown(dropdown, trigger, menu) {
165170 const isOpen = trigger.getAttribute('aria-expanded') === 'true';
166- if (isOpen) closeDropdown(dropdown, trigger, menu);
167- else openDropdown(dropdown, trigger, menu);
171+ if (isOpen) closeDropdown(dropdown, trigger, menu); else openDropdown(dropdown, trigger, menu);
168172}
169173function openDropdown(dropdown, trigger, menu) {
170174 trigger.setAttribute('aria-expanded', 'true');
@@ -179,39 +183,33 @@ function closeDropdown(dropdown, trigger, menu) {
179183
180184function bindDisplaySettings() {
181185 const btn = document.getElementById('display-settings-switch');
182- if (!btn) return;
183-
184- const attach = () => {
185- if (btn.dataset.bound === '1') return;
186- btn.addEventListener('click', () => {
187- const panelEl = document.getElementById('display-setting');
188- if (!panelEl) return;
189- panelEl.classList.toggle('float-panel-closed');
190- });
191- btn.dataset.bound = '1';
192- };
193-
194186 const panel = document.getElementById('display-setting');
195- if (panel) {
196- attach();
197- if (window.__displaySettingsObserver) {
198- window.__displaySettingsObserver.disconnect();
199- window.__displaySettingsObserver = undefined;
200- }
201- return;
202- }
187+ if (!btn || !panel) return;
188+ if (btn.dataset.bound === '1') return;
189+ btn.addEventListener('click', () => {
190+ panel.classList.toggle('float-panel-closed');
191+ });
192+ btn.dataset.bound = '1';
193+ }
194+
195+ function bindNavMenu() {
196+ const btn = document.getElementById('nav-menu-switch');
197+ const panel = document.getElementById('nav-menu-panel');
198+ if (!btn || !panel) return;
199+ if (btn.dataset.bound === '1') return;
200+ btn.addEventListener('click', (event) => {
201+ event.preventDefault();
202+ panel.classList.toggle('float-panel-closed');
203+ });
204+ btn.dataset.bound = '1';
203205
204- if (!window.__displaySettingsObserver) {
205- const observer = new MutationObserver(() => {
206- const nextPanel = document.getElementById('display-setting');
207- if (nextPanel) {
208- attach();
209- observer.disconnect();
210- window.__displaySettingsObserver = undefined;
206+ if (!window.__navMenuOutsideHandler) {
207+ window.__navMenuOutsideHandler = (event) => {
208+ if (!panel.contains(event.target) && event.target !== btn && !btn.contains(event.target)) {
209+ panel.classList.add('float-panel-closed');
211210 }
212- });
213- observer.observe(document.body, { childList: true, subtree: true });
214- window.__displaySettingsObserver = observer;
211+ };
212+ document.addEventListener('click', window.__navMenuOutsideHandler);
215213 }
216214}
217215
@@ -240,8 +238,7 @@ function initSemifullScrollDetection() {
240238 const isHome = navbar.getAttribute('data-is-home') === 'true';
241239 const threshold = 12;
242240 const applyScrolledState = (active) => {
243- if (active) navbar.classList.add('scrolled');
244- else navbar.classList.remove('scrolled');
241+ if (active) navbar.classList.add('scrolled'); else navbar.classList.remove('scrolled');
245242 };
246243 if (!isHome) { applyScrolledState(true); return; }
247244
@@ -251,7 +248,12 @@ function initSemifullScrollDetection() {
251248 applyScrolledState(scrollTop > threshold);
252249 ticking = false;
253250 }
254- function requestTick() { if (!ticking) { requestAnimationFrame(updateNavbarState); ticking = true; } }
251+ function requestTick() {
252+ if (!ticking) {
253+ requestAnimationFrame(updateNavbarState);
254+ ticking = true;
255+ }
256+ }
255257
256258 semifullScrollHandler = requestTick;
257259 window.addEventListener('scroll', requestTick, { passive: true });
@@ -261,6 +263,7 @@ function initSemifullScrollDetection() {
261263function initNavbar() {
262264 bindDropdowns();
263265 bindDisplaySettings();
266+ bindNavMenu();
264267 initSemifullScrollDetection();
265268}
266269
@@ -299,4 +302,4 @@ if (document.readyState === 'loading') {
299302} else {
300303 loadPagefind ();
301304}
302- </script >}
305+ </script >}
0 commit comments