@gsa-tts/graymatter-ui 0.4.8 → 0.4.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsa-tts/graymatter-ui",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -61,9 +61,9 @@
61
61
  "typescript": "5.7.3",
62
62
  "vite": "^6.3.5",
63
63
  "vitest": "^3.0.7",
64
+ "@gsa-tts/graymatter-eslint": "0.0.2",
64
65
  "@gsa-tts/graymatter-typescript-config": "0.0.2",
65
- "@gsa-tts/graymatter-vitest-config": "0.0.1",
66
- "@gsa-tts/graymatter-eslint": "0.0.2"
66
+ "@gsa-tts/graymatter-vitest-config": "0.0.1"
67
67
  },
68
68
  "dependencies": {
69
69
  "@fontsource-variable/archivo": "^5.2.6",
@@ -37,6 +37,9 @@
37
37
  supplementalMenuData,
38
38
  } = $props();
39
39
 
40
+ const helpUrl = supplementalMenuData?.help?.url?.trim();
41
+ const isHelpSelected = $derived(() => get(selectedSubNavItemId) === 'help');
42
+
40
43
  let hydrated = false;
41
44
  let navContainer: HTMLDivElement;
42
45
  let hoveredItem = $state<string | null>(null);
@@ -175,8 +178,30 @@
175
178
  }
176
179
  };
177
180
  }
181
+
182
+ const location = (
183
+ globalThis as { location?: { pathname?: string; origin?: string } }
184
+ ).location;
185
+ if (helpUrl && location?.origin && location?.pathname) {
186
+ const helpPath = getPathname(helpUrl, location.origin);
187
+ if (helpPath && location.pathname === helpPath) {
188
+ selectedSubNavItemId.set('help');
189
+ }
190
+ }
178
191
  });
179
192
 
193
+ function getPathname(url: string, baseOrigin: string) {
194
+ try {
195
+ return new URL(url, baseOrigin).pathname;
196
+ } catch {
197
+ return null;
198
+ }
199
+ }
200
+
201
+ function handleHelpClick() {
202
+ selectedSubNavItemId.set('help');
203
+ }
204
+
180
205
  function toggleNav() {
181
206
  if ($isMenuButtonDisabled) return;
182
207
  navigationStore.toggleExpanded();
@@ -428,21 +453,26 @@
428
453
 
429
454
  {#if profileMenuData}
430
455
  <div class="nav-footer" part="nav-footer">
431
- {#if supplementalMenuData?.help?.url}
456
+ {#if helpUrl}
432
457
  <div
433
458
  class="nav-footer-icon-container"
434
459
  part="nav-footer-icon-container"
435
460
  >
436
461
  <a
437
462
  class="help-link"
438
- class:selected={get(selectedSubNavItemId) === 'help'}
439
- aria-current={get(selectedSubNavItemId) === 'help'}
440
- href={supplementalMenuData.help.url}
463
+ class:selected={isHelpSelected()}
464
+ aria-current={isHelpSelected()}
465
+ href={helpUrl}
441
466
  part="help-link"
442
467
  title="Help"
443
468
  aria-label="Help"
469
+ onclick={handleHelpClick}
444
470
  >
445
- <HelpIcon />
471
+ <div class="icon-container" part="help-icon-container">
472
+ <div class="icon-wrapper">
473
+ <HelpIcon isSelected={isHelpSelected()} />
474
+ </div>
475
+ </div>
446
476
  </a>
447
477
  </div>
448
478
  {/if}
@@ -622,7 +652,8 @@
622
652
  }
623
653
 
624
654
  .menu-button:focus .icon-container,
625
- .nav-item:focus .icon-container {
655
+ .nav-item:focus .icon-container,
656
+ .help-link:focus .icon-container {
626
657
  outline: var(--ai-size-2) solid var(--ai-color-blue-600);
627
658
  outline-offset: var(--ai-size-2);
628
659
  border-radius: var(--ai-size-4);
@@ -630,7 +661,8 @@
630
661
 
631
662
  /* Hide focus outline on click */
632
663
  .menu-button:focus:not(:focus-visible) .icon-container,
633
- .nav-item:focus:not(:focus-visible) .icon-container {
664
+ .nav-item:focus:not(:focus-visible) .icon-container,
665
+ .help-link:focus:not(:focus-visible) .icon-container {
634
666
  outline: none;
635
667
  }
636
668
 
@@ -724,7 +756,9 @@
724
756
  .nav-item.selected .icon-container,
725
757
  .nav-item.selected.hovered .icon-container,
726
758
  .menu-button:hover .icon-container,
727
- .menu-button:active .icon-container {
759
+ .menu-button:active .icon-container,
760
+ .help-link:hover .icon-container,
761
+ .help-link.selected .icon-container {
728
762
  background-color: var(--ai-color-steel-300);
729
763
  border-radius: var(--ai-size-4);
730
764
  }
@@ -765,20 +799,14 @@
765
799
  }
766
800
 
767
801
  .help-link {
768
- border-radius: var(--ai-size-4);
769
- padding: var(--ai-size-4);
770
- transition: background-color var(--ai-transition-ease-medium);
802
+ display: flex;
803
+ align-items: center;
804
+ justify-content: center;
805
+ text-decoration: none;
806
+ color: inherit;
771
807
  }
772
808
 
773
809
  .help-link :global(svg) {
774
810
  display: block;
775
811
  }
776
-
777
- .help-link:hover {
778
- background: var(--ai-color-steel-300);
779
- }
780
-
781
- .help-link:is(.selected) {
782
- background-color: var(--ai-color-steel-300);
783
- }
784
812
  </style>
@@ -3,15 +3,22 @@
3
3
  isMenuButtonDisabled,
4
4
  navigationStore,
5
5
  selectedSubNavItemTitle,
6
+ selectedSubNavItemId,
6
7
  } from '../stores/navigationStore.js';
7
8
  import Logo from './Logo.svelte';
8
9
  import MenuIcon from './icons/MenuIcon.svelte';
10
+ import HelpIcon from './icons/HelpIcon.svelte';
9
11
  import { onMount } from 'svelte';
12
+ import { get } from 'svelte/store';
10
13
 
11
- let { profileMenuData, logoLinkUrl, logoLinkLabel } = $props();
14
+ let { profileMenuData, logoLinkUrl, logoLinkLabel, supplementalMenuData } =
15
+ $props();
12
16
 
13
17
  import ProfileMenu from './ProfileMenu.svelte';
14
18
 
19
+ const helpUrl = supplementalMenuData?.help?.url?.trim();
20
+ const isHelpSelected = $derived(() => get(selectedSubNavItemId) === 'help');
21
+
15
22
  onMount(() => {
16
23
  // Listen for section visibility changes to update header
17
24
  const handleSectionVisible = (event: Event) => {
@@ -72,9 +79,35 @@
72
79
  };
73
80
  });
74
81
 
82
+ onMount(() => {
83
+ const location = (
84
+ globalThis as {
85
+ location?: { pathname?: string; origin?: string };
86
+ }
87
+ ).location;
88
+ if (helpUrl && location?.origin && location?.pathname) {
89
+ const helpPath = getPathname(helpUrl, location.origin);
90
+ if (helpPath && location.pathname === helpPath) {
91
+ selectedSubNavItemId.set('help');
92
+ }
93
+ }
94
+ });
95
+
96
+ function getPathname(url: string, baseOrigin: string) {
97
+ try {
98
+ return new URL(url, baseOrigin).pathname;
99
+ } catch {
100
+ return null;
101
+ }
102
+ }
103
+
75
104
  function toggleMobileNav() {
76
105
  navigationStore.toggleExpanded();
77
106
  }
107
+
108
+ function handleHelpClick() {
109
+ selectedSubNavItemId.set('help');
110
+ }
78
111
  </script>
79
112
 
80
113
  <nav class="mobile-top-nav" part="mobile-top-nav">
@@ -101,6 +134,22 @@
101
134
  <span class="brand-text">{$selectedSubNavItemTitle || ''}</span>
102
135
  </div>
103
136
 
137
+ {#if helpUrl}
138
+ <a
139
+ class="help-button"
140
+ class:selected={isHelpSelected()}
141
+ aria-current={isHelpSelected()}
142
+ href={helpUrl}
143
+ title="Help"
144
+ aria-label="Help"
145
+ onclick={handleHelpClick}
146
+ >
147
+ <span class="icon-container">
148
+ <HelpIcon isSelected={isHelpSelected()} />
149
+ </span>
150
+ </a>
151
+ {/if}
152
+
104
153
  {#if profileMenuData}
105
154
  <ProfileMenu
106
155
  userMeta={profileMenuData?.userMeta}
@@ -184,4 +233,28 @@
184
233
  .menu-button:focus {
185
234
  outline: none;
186
235
  }
236
+
237
+ .help-button {
238
+ display: flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ margin-right: var(--ai-size-4);
242
+ text-decoration: none;
243
+ color: inherit;
244
+ }
245
+
246
+ .help-button .icon-container {
247
+ display: flex;
248
+ align-items: center;
249
+ justify-content: center;
250
+ width: var(--ai-size-40);
251
+ height: var(--ai-size-40);
252
+ border-radius: var(--ai-size-4);
253
+ transition: background-color var(--ai-transition-ease-fast);
254
+ }
255
+
256
+ .help-button:hover .icon-container,
257
+ .help-button.selected .icon-container {
258
+ background-color: var(--ai-color-steel-300);
259
+ }
187
260
  </style>
@@ -8,7 +8,13 @@
8
8
  import MobileTopNav from './MobileTopNav.svelte';
9
9
 
10
10
  // Props for the web component
11
- let { profileMenuData, logoLinkUrl, logoLinkLabel } = $props();
11
+ let { profileMenuData, logoLinkUrl, logoLinkLabel, supplementalMenuData } =
12
+ $props();
12
13
  </script>
13
14
 
14
- <MobileTopNav {profileMenuData} {logoLinkUrl} {logoLinkLabel} />
15
+ <MobileTopNav
16
+ {profileMenuData}
17
+ {logoLinkUrl}
18
+ {logoLinkLabel}
19
+ {supplementalMenuData}
20
+ />
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- export let isHovered: boolean = false;
2
+ export let isSelected = false;
3
3
  </script>
4
4
 
5
5
  <svg
@@ -11,7 +11,7 @@
11
11
  >
12
12
  <path
13
13
  d="M9.87891 7.51884C11.0505 6.49372 12.95 6.49372 14.1215 7.51884C15.2931 8.54397 15.2931 10.206 14.1215 11.2312C13.9176 11.4096 13.6917 11.5569 13.4513 11.6733C12.7056 12.0341 12.0002 12.6716 12.0002 13.5V14.25M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12ZM12 17.25H12.0075V17.2575H12V17.25Z"
14
- stroke={isHovered ? '#000' : '#585C63'}
14
+ stroke={isSelected ? '#000' : '#585C63'}
15
15
  stroke-width="1.5"
16
16
  stroke-linecap="round"
17
17
  stroke-linejoin="round"
@@ -15,6 +15,7 @@ const {
15
15
  showAppIcons = false,
16
16
  disableCodeCopyButtons = false,
17
17
  profileMenuData = null,
18
+ supplementalMenuData = undefined,
18
19
  hideDiscover = true,
19
20
  contentCustomization = undefined,
20
21
  logoLinkUrl,
@@ -65,6 +66,7 @@ const subNavData = generateApiDocumentationNavData(apiDocsMetadata);
65
66
  {description}
66
67
  {openGraphImage}
67
68
  {hideDiscover}
69
+ {supplementalMenuData}
68
70
  >
69
71
  <!-- Initialize navigation state and auto-expand submenu for section pages -->
70
72
  <NavigationInitializer