@gsa-tts/graymatter-ui 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/images/ai-icons/document_text.svg +3 -0
- package/assets/images/ai-icons/main-nav/gear.svg +4 -0
- package/assets/images/ai-icons/main-nav/logout.svg +4 -0
- package/dist/graymatter-ui.js +2181 -1855
- package/dist/graymatter-ui.umd.cjs +21 -20
- package/package.json +2 -2
- package/src/components/DesktopSideNav.svelte +143 -71
- package/src/components/DesktopSideNav.webcomponent.svelte +4 -2
- package/src/components/MobileBottomNav.svelte +4 -1
- package/src/components/MobileBottomNav.webcomponent.svelte +2 -1
- package/src/components/MobileSideMenu.svelte +22 -5
- package/src/components/MobileSideMenu.webcomponent.svelte +3 -1
- package/src/components/MobileTopNav.svelte +13 -73
- package/src/components/MobileTopNav.webcomponent.svelte +2 -2
- package/src/components/ProfileMenu.svelte +241 -0
- package/src/components/UsaBanner.astro +4 -1
- package/src/components/icons/HelpIcon.svelte +18 -0
- package/src/constants/common.ts +2 -1
- package/src/layouts/GlobalAppLayout.astro +36 -10
- package/src/utils/setupMocks.ts +44 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gsa-tts/graymatter-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"@uswds/uswds": "3.12.0",
|
|
65
65
|
"astro": "^5.11.0",
|
|
66
66
|
"slugify": "^1.6.6",
|
|
67
|
-
"@gsa-tts/graymatter-style-tokens": "0.2.
|
|
67
|
+
"@gsa-tts/graymatter-style-tokens": "0.2.3"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
70
|
"build": "gulp update && vite build",
|
|
@@ -18,16 +18,34 @@
|
|
|
18
18
|
import ConsoleIcon from './icons/ConsoleIcon.svelte';
|
|
19
19
|
import ApiIcon from './icons/ApiIcon.svelte';
|
|
20
20
|
import MenuIcon from './icons/MenuIcon.svelte';
|
|
21
|
+
import ProfileMenu from './ProfileMenu.svelte';
|
|
21
22
|
import { getAppUrls } from '../utils/getAppUrls';
|
|
23
|
+
import HelpIcon from './icons/HelpIcon.svelte';
|
|
22
24
|
const urls = getAppUrls();
|
|
23
|
-
|
|
24
|
-
const {
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
ssrSelectedItem = undefined,
|
|
28
|
+
onNavToggle = undefined,
|
|
29
|
+
onNavItemClick = undefined,
|
|
30
|
+
children,
|
|
31
|
+
showAppIcons = true,
|
|
32
|
+
profileMenuData,
|
|
33
|
+
apiUrl,
|
|
34
|
+
chatUrl,
|
|
35
|
+
consoleUrl,
|
|
36
|
+
discoverUrl
|
|
37
|
+
} = $props();
|
|
25
38
|
|
|
26
39
|
let hydrated = false;
|
|
27
40
|
let navContainer: HTMLDivElement;
|
|
28
41
|
let hoveredItem = $state<string | null>(null);
|
|
29
42
|
let menuButtonHovered = $state(false);
|
|
30
43
|
let canHover = $state(false);
|
|
44
|
+
let helpIconHovered = $state(false);
|
|
45
|
+
let discoverNavRef = $state<HTMLAnchorElement | null>(null);
|
|
46
|
+
let chatNavRef = $state<HTMLAnchorElement | null>(null);
|
|
47
|
+
let consoleNavRef = $state<HTMLAnchorElement | null>(null);
|
|
48
|
+
let apiNavRef = $state<HTMLAnchorElement | null>(null);
|
|
31
49
|
|
|
32
50
|
function ssrOrHydrated<T>(hydratedValue: T, ssrValue: T): T {
|
|
33
51
|
return hydrated ? hydratedValue : ssrValue;
|
|
@@ -94,8 +112,22 @@
|
|
|
94
112
|
|
|
95
113
|
window.addEventListener('layoutTransition', handleLayoutTransition);
|
|
96
114
|
|
|
115
|
+
// Add Escape key handler for nav collapse, but ignore if focus is in profile context menu
|
|
116
|
+
const handleEscape = (e: KeyboardEvent) => {
|
|
117
|
+
const active = document.activeElement;
|
|
118
|
+
if (
|
|
119
|
+
e.key === 'Escape' &&
|
|
120
|
+
$isExpanded &&
|
|
121
|
+
!(active && active.closest('.profile-context-menu'))
|
|
122
|
+
) {
|
|
123
|
+
navigationStore.setExpanded(false);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
window.addEventListener('keydown', handleEscape);
|
|
127
|
+
|
|
97
128
|
return () => {
|
|
98
129
|
window.removeEventListener('layoutTransition', handleLayoutTransition);
|
|
130
|
+
window.removeEventListener('keydown', handleEscape);
|
|
99
131
|
};
|
|
100
132
|
}
|
|
101
133
|
});
|
|
@@ -171,6 +203,27 @@
|
|
|
171
203
|
handleNavItemClick(item);
|
|
172
204
|
}
|
|
173
205
|
}
|
|
206
|
+
|
|
207
|
+
function handleNavKeyDown(event: KeyboardEvent, index: number) {
|
|
208
|
+
const navRefs = [discoverNavRef, chatNavRef, consoleNavRef, apiNavRef];
|
|
209
|
+
const navCount = navRefs.length;
|
|
210
|
+
if (!navCount) return;
|
|
211
|
+
if (event.key === 'ArrowDown') {
|
|
212
|
+
event.preventDefault();
|
|
213
|
+
const next = (index + 1) % navCount;
|
|
214
|
+
navRefs[next]?.focus();
|
|
215
|
+
} else if (event.key === 'ArrowUp') {
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
const prev = (index - 1 + navCount) % navCount;
|
|
218
|
+
navRefs[prev]?.focus();
|
|
219
|
+
} else if (event.key === 'Home') {
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
navRefs[0]?.focus();
|
|
222
|
+
} else if (event.key === 'End') {
|
|
223
|
+
event.preventDefault();
|
|
224
|
+
navRefs[navCount - 1]?.focus();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
174
227
|
</script>
|
|
175
228
|
|
|
176
229
|
<div
|
|
@@ -206,6 +259,7 @@
|
|
|
206
259
|
</button>
|
|
207
260
|
</div>
|
|
208
261
|
|
|
262
|
+
{#if showAppIcons}
|
|
209
263
|
<div class="nav-items" part="nav-items">
|
|
210
264
|
<a
|
|
211
265
|
href={discoverUrl || urls.discover}
|
|
@@ -213,11 +267,12 @@
|
|
|
213
267
|
class:selected={isNavSelected('discover')}
|
|
214
268
|
class:hovered={isNavHovered('discover')}
|
|
215
269
|
onclick={() => handleNavItemClick('discover')}
|
|
216
|
-
onkeydown={e => handleKeyDown(e, 'discover')}
|
|
270
|
+
onkeydown={e => { handleKeyDown(e, 'discover'); handleNavKeyDown(e, 0); }}
|
|
217
271
|
onmouseenter={() => (hoveredItem = 'discover')}
|
|
218
272
|
onmouseleave={() => (hoveredItem = null)}
|
|
219
273
|
aria-current={isNavSelected('discover') ? 'page' : undefined}
|
|
220
274
|
part="discover-item"
|
|
275
|
+
bind:this={discoverNavRef}
|
|
221
276
|
>
|
|
222
277
|
<div class="icon-container">
|
|
223
278
|
<div class="icon-wrapper">
|
|
@@ -232,11 +287,12 @@
|
|
|
232
287
|
class:selected={isNavSelected('chat')}
|
|
233
288
|
class:hovered={isNavHovered('chat')}
|
|
234
289
|
onclick={() => handleNavItemClick('chat')}
|
|
235
|
-
onkeydown={e => handleKeyDown(e, 'chat')}
|
|
290
|
+
onkeydown={e => { handleKeyDown(e, 'chat'); handleNavKeyDown(e, 1); }}
|
|
236
291
|
onmouseenter={() => (hoveredItem = 'chat')}
|
|
237
292
|
onmouseleave={() => (hoveredItem = null)}
|
|
238
293
|
aria-current={isNavSelected('chat') ? 'page' : undefined}
|
|
239
294
|
part="chat-item"
|
|
295
|
+
bind:this={chatNavRef}
|
|
240
296
|
>
|
|
241
297
|
<div class="icon-container">
|
|
242
298
|
<div class="icon-wrapper">
|
|
@@ -252,11 +308,12 @@
|
|
|
252
308
|
class:selected={isNavSelected('console')}
|
|
253
309
|
class:hovered={isNavHovered('console')}
|
|
254
310
|
onclick={() => handleNavItemClick('console')}
|
|
255
|
-
onkeydown={e => handleKeyDown(e, 'console')}
|
|
311
|
+
onkeydown={e => { handleKeyDown(e, 'console'); handleNavKeyDown(e, 2); }}
|
|
256
312
|
onmouseenter={() => (hoveredItem = 'console')}
|
|
257
313
|
onmouseleave={() => (hoveredItem = null)}
|
|
258
314
|
aria-current={isNavSelected('console') ? 'page' : undefined}
|
|
259
315
|
part="console-item"
|
|
316
|
+
bind:this={consoleNavRef}
|
|
260
317
|
>
|
|
261
318
|
<div class="icon-container">
|
|
262
319
|
<div class="icon-wrapper">
|
|
@@ -272,11 +329,12 @@
|
|
|
272
329
|
class:selected={isNavSelected('api')}
|
|
273
330
|
class:hovered={isNavHovered('api')}
|
|
274
331
|
onclick={() => handleNavItemClick('api')}
|
|
275
|
-
onkeydown={e => handleKeyDown(e, 'api')}
|
|
332
|
+
onkeydown={e => { handleKeyDown(e, 'api'); handleNavKeyDown(e, 3); }}
|
|
276
333
|
onmouseenter={() => (hoveredItem = 'api')}
|
|
277
334
|
onmouseleave={() => (hoveredItem = null)}
|
|
278
335
|
aria-current={isNavSelected('api') ? 'page' : undefined}
|
|
279
336
|
part="api-item"
|
|
337
|
+
bind:this={apiNavRef}
|
|
280
338
|
>
|
|
281
339
|
<div class="icon-container">
|
|
282
340
|
<div class="icon-wrapper">
|
|
@@ -286,27 +344,37 @@
|
|
|
286
344
|
<span class="nav-text">API</span>
|
|
287
345
|
</a>
|
|
288
346
|
</div>
|
|
347
|
+
{/if}
|
|
289
348
|
</div>
|
|
290
349
|
|
|
350
|
+
{#if profileMenuData}
|
|
291
351
|
<div class="nav-footer" part="nav-footer">
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
<span class="user-initials">ZW</span>
|
|
352
|
+
<div class="nav-footer-icon-container">
|
|
353
|
+
<button
|
|
354
|
+
type="button"
|
|
355
|
+
class="nav-footer-icon-inner"
|
|
356
|
+
tabindex="0"
|
|
357
|
+
aria-label="Help"
|
|
358
|
+
onmouseenter={() => (helpIconHovered = true)}
|
|
359
|
+
onmouseleave={() => (helpIconHovered = false)}
|
|
360
|
+
onfocus={() => (helpIconHovered = true)}
|
|
361
|
+
onblur={() => (helpIconHovered = false)}
|
|
362
|
+
>
|
|
363
|
+
<div class="nav-footer-icon-svg-wrapper">
|
|
364
|
+
<HelpIcon isHovered={helpIconHovered} />
|
|
306
365
|
</div>
|
|
307
|
-
</
|
|
308
|
-
</
|
|
366
|
+
</button>
|
|
367
|
+
</div>
|
|
368
|
+
<div class="nav-footer-profile-container">
|
|
369
|
+
|
|
370
|
+
<ProfileMenu
|
|
371
|
+
userMeta={profileMenuData?.userMeta}
|
|
372
|
+
menuItems={profileMenuData?.menuItems}
|
|
373
|
+
ariaLabel="Open profile menu"
|
|
374
|
+
/>
|
|
375
|
+
</div>
|
|
309
376
|
</div>
|
|
377
|
+
{/if}
|
|
310
378
|
</div>
|
|
311
379
|
|
|
312
380
|
<div class="expanded-content drawer-anim" class:open={sideNavExpanded()} part="expanded-content">
|
|
@@ -344,13 +412,6 @@
|
|
|
344
412
|
height: var(--ai-size-72);
|
|
345
413
|
}
|
|
346
414
|
|
|
347
|
-
.user-profile-container,
|
|
348
|
-
.user-profile-icon {
|
|
349
|
-
display: flex;
|
|
350
|
-
align-items: center;
|
|
351
|
-
justify-content: center;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
415
|
.menu-button {
|
|
355
416
|
display: flex;
|
|
356
417
|
align-items: center;
|
|
@@ -368,18 +429,6 @@
|
|
|
368
429
|
opacity: 0.5;
|
|
369
430
|
background-color: transparent;
|
|
370
431
|
}
|
|
371
|
-
|
|
372
|
-
.user-profile-icon {
|
|
373
|
-
border-radius: var(--ai-size-6);
|
|
374
|
-
background: var(--ai-color-steel-700);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
.user-initials {
|
|
378
|
-
color: var(--ai-color-white);
|
|
379
|
-
text-align: center;
|
|
380
|
-
font-weight: 400;
|
|
381
|
-
line-height: 1.3;
|
|
382
|
-
}
|
|
383
432
|
|
|
384
433
|
.side-nav-container {
|
|
385
434
|
display: flex;
|
|
@@ -452,36 +501,6 @@
|
|
|
452
501
|
margin-top: auto;
|
|
453
502
|
}
|
|
454
503
|
|
|
455
|
-
.user-profile-button {
|
|
456
|
-
background: none;
|
|
457
|
-
border: none;
|
|
458
|
-
padding: 0;
|
|
459
|
-
cursor: pointer;
|
|
460
|
-
display: flex;
|
|
461
|
-
justify-content: center;
|
|
462
|
-
align-items: center;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
.user-profile-button:focus {
|
|
466
|
-
outline: var(--ai-size-2) solid var(--ai-color-blue-600);
|
|
467
|
-
outline-offset: var(--ai-size-2);
|
|
468
|
-
border-radius: var(--ai-size-4);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
.user-profile-container {
|
|
472
|
-
width: var(--ai-size-40);
|
|
473
|
-
height: var(--ai-size-40);
|
|
474
|
-
aspect-ratio: 1/1;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
.user-profile-icon {
|
|
478
|
-
padding: 0.5556rem 0.4931rem 0.5694rem 0.3819rem;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
.user-initials {
|
|
482
|
-
font-size: var(--ai-size-16);
|
|
483
|
-
font-style: normal;
|
|
484
|
-
}
|
|
485
504
|
|
|
486
505
|
.menu-section {
|
|
487
506
|
display: flex;
|
|
@@ -632,4 +651,57 @@
|
|
|
632
651
|
color: var(--ai-color-neutral-700);
|
|
633
652
|
}
|
|
634
653
|
|
|
654
|
+
.nav-footer-icon-container {
|
|
655
|
+
display: flex;
|
|
656
|
+
padding: 0 var(--ai-size-4);
|
|
657
|
+
flex-direction: column;
|
|
658
|
+
justify-content: center;
|
|
659
|
+
align-items: center;
|
|
660
|
+
gap: var(--ai-size-4);
|
|
661
|
+
align-self: stretch;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.nav-footer-profile-container {
|
|
665
|
+
display: flex;
|
|
666
|
+
padding: var(--ai-size-8) var(--ai-size-4);
|
|
667
|
+
flex-direction: column;
|
|
668
|
+
justify-content: center;
|
|
669
|
+
align-items: center;
|
|
670
|
+
gap: var(--ai-size-4);
|
|
671
|
+
align-self: stretch;
|
|
672
|
+
}
|
|
673
|
+
.nav-footer-icon-inner {
|
|
674
|
+
display: flex;
|
|
675
|
+
width: var(--ai-size-40);
|
|
676
|
+
height: var(--ai-size-40);
|
|
677
|
+
padding: var(--ai-size-8);
|
|
678
|
+
justify-content: center;
|
|
679
|
+
align-items: center;
|
|
680
|
+
border-radius: var(--ai-size-4);
|
|
681
|
+
background: none;
|
|
682
|
+
border: none;
|
|
683
|
+
cursor: pointer;
|
|
684
|
+
transition: background-color 0.3s ease, border-radius 0.3s ease;
|
|
685
|
+
}
|
|
686
|
+
.nav-footer-icon-inner:hover,
|
|
687
|
+
.nav-footer-icon-inner:focus {
|
|
688
|
+
background-color: var(--ai-color-steel-300);
|
|
689
|
+
border-radius: var(--ai-size-4);
|
|
690
|
+
}
|
|
691
|
+
.nav-footer-icon-inner:focus {
|
|
692
|
+
outline: none;
|
|
693
|
+
}
|
|
694
|
+
.nav-footer-icon-inner:focus-visible {
|
|
695
|
+
outline: var(--ai-size-2) solid var(--ai-color-blue-600);
|
|
696
|
+
outline-offset: var(--ai-size-2);
|
|
697
|
+
border-radius: var(--ai-size-4);
|
|
698
|
+
}
|
|
699
|
+
.nav-footer-icon-svg-wrapper {
|
|
700
|
+
width: var(--ai-size-24);
|
|
701
|
+
height: var(--ai-size-24);
|
|
702
|
+
flex-shrink: 0;
|
|
703
|
+
display: flex;
|
|
704
|
+
align-items: center;
|
|
705
|
+
justify-content: center;
|
|
706
|
+
}
|
|
635
707
|
</style>
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
ssrSelectedItem,
|
|
11
11
|
onNavToggle,
|
|
12
12
|
onNavItemClick,
|
|
13
|
-
|
|
13
|
+
showAppIcons = true,
|
|
14
|
+
profileMenuData,
|
|
14
15
|
apiUrl,
|
|
15
16
|
chatUrl,
|
|
16
17
|
consoleUrl,
|
|
@@ -22,7 +23,8 @@
|
|
|
22
23
|
{ssrSelectedItem}
|
|
23
24
|
{onNavToggle}
|
|
24
25
|
{onNavItemClick}
|
|
25
|
-
{
|
|
26
|
+
{showAppIcons}
|
|
27
|
+
{profileMenuData}
|
|
26
28
|
{apiUrl}
|
|
27
29
|
{chatUrl}
|
|
28
30
|
{consoleUrl}
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
import { getAppUrls } from '../utils/getAppUrls';
|
|
9
9
|
import slugify from "slugify";
|
|
10
10
|
|
|
11
|
-
let {onNavItemClick, apiUrl, chatUrl, consoleUrl, discoverUrl}: {
|
|
11
|
+
let {onNavItemClick, showAppIcons = true, apiUrl, chatUrl, consoleUrl, discoverUrl}: {
|
|
12
12
|
onNavItemClick?: (data: { item: NavigationItem; expanded: boolean }) => void,
|
|
13
|
+
showAppIcons: boolean,
|
|
13
14
|
apiUrl?: string,
|
|
14
15
|
chatUrl?: string,
|
|
15
16
|
consoleUrl?: string,
|
|
@@ -85,6 +86,7 @@
|
|
|
85
86
|
}
|
|
86
87
|
</script>
|
|
87
88
|
|
|
89
|
+
{#if showAppIcons}
|
|
88
90
|
<nav class="mobile-bottom-nav" part="mobile-bottom-nav">
|
|
89
91
|
{#each navItems as navItem}
|
|
90
92
|
<a
|
|
@@ -103,6 +105,7 @@
|
|
|
103
105
|
</a>
|
|
104
106
|
{/each}
|
|
105
107
|
</nav>
|
|
108
|
+
{/if}
|
|
106
109
|
|
|
107
110
|
<style>
|
|
108
111
|
.nav-item {
|
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
import MobileBottomNav from './MobileBottomNav.svelte';
|
|
7
7
|
|
|
8
8
|
// Props for the web component
|
|
9
|
-
let { onNavItemClick, apiUrl, chatUrl, consoleUrl, discoverUrl } = $props();
|
|
9
|
+
let { onNavItemClick, showAppIcons = true, apiUrl, chatUrl, consoleUrl, discoverUrl } = $props();
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<MobileBottomNav
|
|
13
13
|
{onNavItemClick}
|
|
14
|
+
{showAppIcons}
|
|
14
15
|
{apiUrl}
|
|
15
16
|
{chatUrl}
|
|
16
17
|
{consoleUrl}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
let sideMenuElement: HTMLDivElement;
|
|
8
8
|
let pendingMobileHash: string | null = null;
|
|
9
|
+
|
|
10
|
+
let { logoLinkUrl, logoLinkLabel } = $props();
|
|
9
11
|
|
|
10
12
|
function handleMobileAnchorNavigate(event: Event) {
|
|
11
13
|
const customEvent = event as CustomEvent;
|
|
@@ -90,7 +92,12 @@
|
|
|
90
92
|
window.addEventListener('layoutTransition', handleLayoutTransition);
|
|
91
93
|
|
|
92
94
|
const handleEscape = (e: KeyboardEvent) => {
|
|
93
|
-
|
|
95
|
+
const active = document.activeElement;
|
|
96
|
+
if (
|
|
97
|
+
e.key === 'Escape' &&
|
|
98
|
+
$isExpanded &&
|
|
99
|
+
!(active && active.closest('.profile-context-menu'))
|
|
100
|
+
) {
|
|
94
101
|
closeMobileMenu();
|
|
95
102
|
}
|
|
96
103
|
};
|
|
@@ -117,8 +124,8 @@
|
|
|
117
124
|
tabindex="0"
|
|
118
125
|
aria-label="Close navigation menu (Escape key supported)"
|
|
119
126
|
part="mobile-overlay"
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
onclick={closeMobileMenu}
|
|
128
|
+
onkeydown={(e) => {
|
|
122
129
|
if (e.key === 'Enter' || e.key === ' ') closeMobileMenu();
|
|
123
130
|
}}
|
|
124
131
|
></div>
|
|
@@ -128,9 +135,15 @@
|
|
|
128
135
|
<div class="mobile-side-menu" class:open={$isExpanded} bind:this={sideMenuElement} part="mobile-side-menu">
|
|
129
136
|
<div class="menu-header" part="menu-header">
|
|
130
137
|
<div class="logo-container" part="logo-container">
|
|
131
|
-
|
|
138
|
+
{#if logoLinkUrl}
|
|
139
|
+
<a href={logoLinkUrl} part="logo-link" class="logo-link" aria-label={logoLinkLabel}>
|
|
140
|
+
<Logo width={75} height={22}/>
|
|
141
|
+
</a>
|
|
142
|
+
{:else}
|
|
143
|
+
<Logo width={75} height={22}/>
|
|
144
|
+
{/if}
|
|
132
145
|
</div>
|
|
133
|
-
<button class="close-button"
|
|
146
|
+
<button class="close-button" onclick={closeMobileMenu} aria-label="Close navigation menu" part="close-button">
|
|
134
147
|
<CloseIcon />
|
|
135
148
|
</button>
|
|
136
149
|
</div>
|
|
@@ -192,6 +205,10 @@
|
|
|
192
205
|
.mobile-side-menu .logo-container {
|
|
193
206
|
display: flex;
|
|
194
207
|
}
|
|
208
|
+
|
|
209
|
+
.logo-link {
|
|
210
|
+
display: flex;
|
|
211
|
+
}
|
|
195
212
|
|
|
196
213
|
.close-button {
|
|
197
214
|
display: flex;
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
import MenuIcon from './icons/MenuIcon.svelte';
|
|
9
9
|
import { onMount } from 'svelte';
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
let { profileMenuData } = $props();
|
|
12
|
+
|
|
13
|
+
import ProfileMenu from './ProfileMenu.svelte';
|
|
12
14
|
|
|
13
15
|
onMount(() => {
|
|
14
16
|
// Listen for section visibility changes to update header
|
|
@@ -66,9 +68,6 @@
|
|
|
66
68
|
navigationStore.toggleExpanded();
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
function handleProfileClick() {
|
|
70
|
-
onProfileClick?.();
|
|
71
|
-
}
|
|
72
71
|
</script>
|
|
73
72
|
|
|
74
73
|
<nav class="mobile-top-nav" part="mobile-top-nav">
|
|
@@ -76,7 +75,7 @@
|
|
|
76
75
|
class="menu-button"
|
|
77
76
|
class:disabled={$isMenuButtonDisabled}
|
|
78
77
|
disabled={$isMenuButtonDisabled}
|
|
79
|
-
|
|
78
|
+
onclick={toggleMobileNav}
|
|
80
79
|
aria-label="Open navigation menu"
|
|
81
80
|
part="menu-button"
|
|
82
81
|
>
|
|
@@ -88,30 +87,19 @@
|
|
|
88
87
|
<span class="brand-text">{$selectedSubNavItemTitle || ''}</span>
|
|
89
88
|
</div>
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<span class="user-initials">ZW</span>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
</button>
|
|
90
|
+
{#if profileMenuData}
|
|
91
|
+
<ProfileMenu
|
|
92
|
+
userMeta={profileMenuData?.userMeta}
|
|
93
|
+
menuItems={profileMenuData?.menuItems}
|
|
94
|
+
ariaLabel="Open profile menu"
|
|
95
|
+
menuAlign="mobile"
|
|
96
|
+
/>
|
|
97
|
+
{/if}
|
|
103
98
|
</nav>
|
|
104
99
|
|
|
105
100
|
<style>
|
|
106
|
-
.user-profile-container,
|
|
107
|
-
.user-profile-icon {
|
|
108
|
-
display: flex;
|
|
109
|
-
align-items: center;
|
|
110
|
-
justify-content: center;
|
|
111
|
-
}
|
|
112
101
|
|
|
113
|
-
.menu-button
|
|
114
|
-
.profile-button {
|
|
102
|
+
.menu-button {
|
|
115
103
|
display: flex;
|
|
116
104
|
align-items: center;
|
|
117
105
|
justify-content: center;
|
|
@@ -129,18 +117,6 @@
|
|
|
129
117
|
background-color: transparent;
|
|
130
118
|
}
|
|
131
119
|
|
|
132
|
-
.user-profile-icon {
|
|
133
|
-
border-radius: var(--ai-size-6);
|
|
134
|
-
background: var(--ai-color-steel-700);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.user-initials {
|
|
138
|
-
color: var(--ai-color-white);
|
|
139
|
-
text-align: center;
|
|
140
|
-
font-weight: 400;
|
|
141
|
-
line-height: 1.3;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
120
|
.mobile-top-nav .menu-button {
|
|
145
121
|
width: var(--ai-size-40);
|
|
146
122
|
height: var(--ai-size-40);
|
|
@@ -173,42 +149,6 @@
|
|
|
173
149
|
font-weight: 600;
|
|
174
150
|
line-height: 1;
|
|
175
151
|
}
|
|
176
|
-
|
|
177
|
-
.profile-button {
|
|
178
|
-
width: var(--ai-size-40);
|
|
179
|
-
height: var(--ai-size-40);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.profile-button:hover {
|
|
183
|
-
background-color: var(--ai-color-neutral-100);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
.mobile-top-nav .user-profile-icon {
|
|
187
|
-
width: var(--ai-size-32);
|
|
188
|
-
height: var(--ai-size-32);
|
|
189
|
-
border-radius: 0.3125rem;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.mobile-top-nav .user-initials {
|
|
193
|
-
font-size: var(--ai-size-14);
|
|
194
|
-
line-height: 1;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
.user-profile-container {
|
|
198
|
-
width: var(--ai-size-40);
|
|
199
|
-
height: var(--ai-size-40);
|
|
200
|
-
aspect-ratio: 1/1;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.user-profile-icon {
|
|
204
|
-
padding: 0.5556rem 0.4931rem 0.5694rem 0.3819rem;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
.user-initials {
|
|
208
|
-
font-size: var(--ai-size-16);
|
|
209
|
-
font-style: normal;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
152
|
.menu-button {
|
|
213
153
|
display: flex;
|
|
214
154
|
padding: var(--ai-size-4);
|