Skip to content

Commit e055da4

Browse files
Improve navbar overflow handling and simplify link width measurement
1 parent 4220475 commit e055da4

File tree

1 file changed

+113
-35
lines changed

1 file changed

+113
-35
lines changed

src/components/navigation/Header.svelte

Lines changed: 113 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@
55
PUBLIC_NAV_BAR_LINKS
66
} from '$env/static/public';
77
8+
import { onMount } from 'svelte';
89
import ThemeSwitcher from '$lib/ThemeSwitch/ThemeSwitcher.svelte';
910
import MobileMenu from './MobileMenu.svelte';
1011
1112
let isMobileMenuOpen = $state(false);
13+
let shouldShowMobile = $state(false);
14+
let navContainer;
15+
let linksContainer;
16+
let linksWidthCache = 0;
1217
1318
let headerLinks = $state(null);
1419
@@ -19,57 +24,130 @@
1924
function toggleNavbar() {
2025
isMobileMenuOpen = !isMobileMenuOpen;
2126
}
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+
});
2293
</script>
2394
2495
<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}
2698
>
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">
30102
<a href="/" class="block">
31103
<img src={PUBLIC_OBA_LOGO_URL} alt={PUBLIC_OBA_REGION_NAME} class="h-10 rounded-sm" />
32104
</a>
33105
<a href="/" class="block text-xl font-extrabold">
34106
{PUBLIC_OBA_REGION_NAME}
35107
</a>
36108
</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>
56109
</div>
57110
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>
67124
</div>
68-
</div>
125+
{/if}
69126
</div>
70127
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}
73151
</div>
74152
</div>
75153

0 commit comments

Comments
 (0)