|
5 | 5 | PUBLIC_NAV_BAR_LINKS
|
6 | 6 | } from '$env/static/public';
|
7 | 7 |
|
| 8 | + import { onMount } from 'svelte'; |
8 | 9 | import ThemeSwitcher from '$lib/ThemeSwitch/ThemeSwitcher.svelte';
|
9 | 10 | import MobileMenu from './MobileMenu.svelte';
|
10 | 11 |
|
11 | 12 | let isMobileMenuOpen = $state(false);
|
| 13 | + let shouldShowMobile = $state(false); |
| 14 | + let navContainer; |
| 15 | + let linksContainer; |
| 16 | + let linksWidthCache = 0; |
12 | 17 |
|
13 | 18 | let headerLinks = $state(null);
|
14 | 19 |
|
|
19 | 24 | function toggleNavbar() {
|
20 | 25 | isMobileMenuOpen = !isMobileMenuOpen;
|
21 | 26 | }
|
| 27 | +
|
| 28 | + function checkOverflow() { |
| 29 | + if (!navContainer) return; |
| 30 | +
|
| 31 | + const navWidth = navContainer.clientWidth; |
| 32 | + const logoWidth = navContainer.querySelector('.logo-container')?.clientWidth || 0; |
| 33 | + const themeWidth = 48; |
| 34 | + const availableWidth = navWidth - logoWidth - themeWidth - 32; |
| 35 | +
|
| 36 | + let linksWidth = 0; |
| 37 | + if (linksContainer) { |
| 38 | + linksWidth = linksContainer.scrollWidth; |
| 39 | + linksWidthCache = linksWidth; |
| 40 | + } else { |
| 41 | + linksWidth = linksWidthCache; |
| 42 | + } |
| 43 | +
|
| 44 | + const newShouldShowMobileMenu = linksWidth > availableWidth; |
| 45 | +
|
| 46 | + if (shouldShowMobile !== newShouldShowMobileMenu) { |
| 47 | + shouldShowMobile = newShouldShowMobileMenu; |
| 48 | + } |
| 49 | + } |
| 50 | +
|
| 51 | + function measureLinksWidth() { |
| 52 | + if (headerLinks && !linksContainer) { |
| 53 | + const tempDiv = document.createElement('div'); |
| 54 | + tempDiv.style.position = 'absolute'; |
| 55 | + tempDiv.style.visibility = 'hidden'; |
| 56 | + tempDiv.style.display = 'flex'; |
| 57 | + tempDiv.style.gap = '1rem'; |
| 58 | +
|
| 59 | + if (Object.keys(headerLinks).length > 0) { |
| 60 | + Object.keys(headerLinks).forEach((key) => { |
| 61 | + const linkDiv = document.createElement('div'); |
| 62 | + linkDiv.style.padding = '0.25rem 0.5rem'; |
| 63 | + linkDiv.style.flexShrink = '0'; |
| 64 | + linkDiv.textContent = key; |
| 65 | + tempDiv.appendChild(linkDiv); |
| 66 | + }); |
| 67 | + } else { |
| 68 | + const linkDiv = document.createElement('div'); |
| 69 | + linkDiv.style.width = '600px'; |
| 70 | + tempDiv.appendChild(linkDiv); |
| 71 | + } |
| 72 | +
|
| 73 | + document.body.appendChild(tempDiv); |
| 74 | + linksWidthCache = tempDiv.scrollWidth; |
| 75 | + document.body.removeChild(tempDiv); |
| 76 | + } |
| 77 | + } |
| 78 | +
|
| 79 | + onMount(() => { |
| 80 | + measureLinksWidth(); |
| 81 | + checkOverflow(); |
| 82 | +
|
| 83 | + const headerResizeObserver = new ResizeObserver(() => { |
| 84 | + checkOverflow(); |
| 85 | + }); |
| 86 | +
|
| 87 | + headerResizeObserver.observe(navContainer); |
| 88 | +
|
| 89 | + return () => { |
| 90 | + headerResizeObserver.disconnect(); |
| 91 | + }; |
| 92 | + }); |
22 | 93 | </script>
|
23 | 94 |
|
24 | 95 | <div
|
25 |
| - class="bg-blur-md flex items-center justify-between border-b border-gray-500 bg-white/80 px-4 dark:bg-black dark:text-white md:flex-row md:px-8" |
| 96 | + class="bg-blur-md bg-white/80 dark:bg-black dark:text-white md:flex-row md:px-8 flex items-center justify-between px-4 border-b border-gray-500" |
| 97 | + bind:this={navContainer} |
26 | 98 | >
|
27 |
| - <div class="flex flex-1 items-center justify-between md:flex-none"> |
28 |
| - <div class="flex w-full justify-between gap-4 px-2 py-2 md:w-auto"> |
29 |
| - <div class="flex items-center justify-center gap-x-2"> |
| 99 | + <div class="md:flex-none flex items-center justify-between flex-1"> |
| 100 | + <div class="logo-container md:w-auto flex items-center w-full gap-4 px-2 py-2"> |
| 101 | + <div class="gap-x-2 flex items-center justify-center"> |
30 | 102 | <a href="/" class="block">
|
31 | 103 | <img src={PUBLIC_OBA_LOGO_URL} alt={PUBLIC_OBA_REGION_NAME} class="h-10 rounded-sm" />
|
32 | 104 | </a>
|
33 | 105 | <a href="/" class="block text-xl font-extrabold">
|
34 | 106 | {PUBLIC_OBA_REGION_NAME}
|
35 | 107 | </a>
|
36 | 108 | </div>
|
37 |
| - |
38 |
| - <div class="flex items-center justify-end md:hidden"> |
39 |
| - <button onclick={toggleNavbar} aria-label="Toggle navigation menu"> |
40 |
| - <svg |
41 |
| - class="burger-icon h-6 w-6 text-gray-900 dark:text-white" |
42 |
| - fill="none" |
43 |
| - stroke="currentColor" |
44 |
| - viewBox="0 0 24 24" |
45 |
| - xmlns="http://www.w3.org/2000/svg" |
46 |
| - > |
47 |
| - <path |
48 |
| - stroke-linecap="round" |
49 |
| - stroke-linejoin="round" |
50 |
| - stroke-width="2" |
51 |
| - d="M4 6h16M4 12h16m-7 6h7" |
52 |
| - ></path> |
53 |
| - </svg> |
54 |
| - </button> |
55 |
| - </div> |
56 | 109 | </div>
|
57 | 110 |
|
58 |
| - <div class="hidden items-center gap-4 px-2 py-2 md:flex"> |
59 |
| - <div class="flex gap-x-4"> |
60 |
| - {#each Object.entries(headerLinks) as [key, value]} |
61 |
| - <div class="rounded-md border bg-white/80 dark:bg-gray-800"> |
62 |
| - <a href={value} class="block px-2 py-1 font-semibold text-gray-900 dark:text-white" |
63 |
| - >{key}</a |
64 |
| - > |
65 |
| - </div> |
66 |
| - {/each} |
| 111 | + {#if !shouldShowMobile} |
| 112 | + <div class="flex items-center px-2 py-2" bind:this={linksContainer}> |
| 113 | + <div class="no-scrollbar gap-x-4 flex overflow-x-auto"> |
| 114 | + {#if headerLinks && Object.keys(headerLinks).length > 0} |
| 115 | + {#each Object.entries(headerLinks) as [key, value]} |
| 116 | + <div class="bg-white/80 dark:bg-gray-800 flex-shrink-0 border rounded-md"> |
| 117 | + <a href={value} class="dark:text-white block px-2 py-1 font-semibold text-gray-900" |
| 118 | + >{key}</a |
| 119 | + > |
| 120 | + </div> |
| 121 | + {/each} |
| 122 | + {/if} |
| 123 | + </div> |
67 | 124 | </div>
|
68 |
| - </div> |
| 125 | + {/if} |
69 | 126 | </div>
|
70 | 127 |
|
71 |
| - <div class="hidden md:flex"> |
72 |
| - <ThemeSwitcher /> |
| 128 | + <div class="flex items-center"> |
| 129 | + {#if shouldShowMobile} |
| 130 | + <button onclick={toggleNavbar} aria-label="Toggle navigation menu" class="mr-2"> |
| 131 | + <svg |
| 132 | + class="burger-icon dark:text-white w-6 h-6 text-gray-900" |
| 133 | + fill="none" |
| 134 | + stroke="currentColor" |
| 135 | + viewBox="0 0 24 24" |
| 136 | + xmlns="http://www.w3.org/2000/svg" |
| 137 | + > |
| 138 | + <path |
| 139 | + stroke-linecap="round" |
| 140 | + stroke-linejoin="round" |
| 141 | + stroke-width="2" |
| 142 | + d="M4 6h16M4 12h16m-7 6h7" |
| 143 | + ></path> |
| 144 | + </svg> |
| 145 | + </button> |
| 146 | + {:else} |
| 147 | + <div class={shouldShowMobile ? '' : 'flex'}> |
| 148 | + <ThemeSwitcher /> |
| 149 | + </div> |
| 150 | + {/if} |
73 | 151 | </div>
|
74 | 152 | </div>
|
75 | 153 |
|
|
0 commit comments